<!DOCTYPE html>
<html lang="en-US" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>daily study | daily study</title>
    <meta name="description" content="无 FUCK 说">
    <link rel="preload stylesheet" href="/assets/style.1ac9090e.css" as="style">
    <link rel="modulepreload" href="/assets/app.db63b7d7.js">
    <link rel="modulepreload" href="/assets/interview_vue.md.76dce706.lean.js">
    
    <script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-c3073c68><!--[--><!--]--><!--[--><span tabindex="-1" data-v-151f2593></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-151f2593> Skip to content </a><!--]--><!----><header class="VPNav" data-v-c3073c68 data-v-0fa0e57d><div class="VPNavBar has-sidebar" data-v-0fa0e57d data-v-be450ad9><div class="container" data-v-be450ad9><div class="title" data-v-be450ad9><div class="VPNavBarTitle has-sidebar" data-v-be450ad9 data-v-6d2fb2d9><a class="title" href="/" data-v-6d2fb2d9><!--[--><!--]--><!--[--><img class="VPImage logo" src="/coll.png" alt data-v-6db2186b><!--]--><!--[--> <!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-be450ad9><div class="curtain" data-v-be450ad9></div><div class="content-body" data-v-be450ad9><!--[--><!--]--><!----><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-be450ad9 data-v-bdedfc22><span id="main-nav-aria-label" class="visually-hidden" data-v-bdedfc22>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" data-v-bdedfc22 data-v-f2ec7ecf data-v-a8b5c975><!--[-->HOME<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/article/" data-v-bdedfc22 data-v-f2ec7ecf data-v-a8b5c975><!--[-->随笔<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/engineering/" data-v-bdedfc22 data-v-f2ec7ecf data-v-a8b5c975><!--[-->工程化<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink active" href="/interview/" data-v-bdedfc22 data-v-f2ec7ecf data-v-a8b5c975><!--[-->前端笔面试题<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/javascript/" data-v-bdedfc22 data-v-f2ec7ecf data-v-a8b5c975><!--[-->JS<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/vue/" data-v-bdedfc22 data-v-f2ec7ecf data-v-a8b5c975><!--[-->Vue<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/node/" data-v-bdedfc22 data-v-f2ec7ecf data-v-a8b5c975><!--[-->Node<!--]--><!----></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-be450ad9 data-v-da3f667a><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" aria-label="toggle dark mode" aria-checked="false" data-v-da3f667a data-v-0d529b6d data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-0d529b6d><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-0d529b6d><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-be450ad9 data-v-2ab2a029 data-v-f6988cfb><!--[--><a class="VPSocialLink" href="https://github.com/hmfy" target="_blank" rel="noopener" data-v-f6988cfb data-v-e57698f6><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-be450ad9 data-v-66bb1f24 data-v-96001b6b><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-96001b6b><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-96001b6b><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-96001b6b><div class="VPMenu" data-v-96001b6b data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-66bb1f24><div class="item appearance" data-v-66bb1f24><p class="label" data-v-66bb1f24>Appearance</p><div class="appearance-action" data-v-66bb1f24><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" aria-label="toggle dark mode" aria-checked="false" data-v-66bb1f24 data-v-0d529b6d data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-0d529b6d><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-0d529b6d><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div></div></div><div class="group" data-v-66bb1f24><div class="item social-links" data-v-66bb1f24><div class="VPSocialLinks social-links-list" data-v-66bb1f24 data-v-f6988cfb><!--[--><a class="VPSocialLink" href="https://github.com/hmfy" target="_blank" rel="noopener" data-v-f6988cfb data-v-e57698f6><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-be450ad9 data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><!----></header><div class="VPLocalNav" data-v-c3073c68 data-v-2817d72e><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-2817d72e><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="menu-icon" data-v-2817d72e><path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"></path><path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"></path><path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"></path><path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"></path></svg><span class="menu-text" data-v-2817d72e>Menu</span></button><a class="top-link" href="#" data-v-2817d72e>Return to top</a></div><aside class="VPSidebar" data-v-c3073c68 data-v-c79ccefa><div class="curtain" data-v-c79ccefa></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-c79ccefa><span class="visually-hidden" id="sidebar-aria-label" data-v-c79ccefa> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-c79ccefa><section class="VPSidebarItem level-0 has-active" data-v-c79ccefa data-v-983f6b21><!----><div class="items" data-v-983f6b21><!--[--><div class="VPSidebarItem level-1 is-link" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/index.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>简介</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/css-note.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>css-note</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/javascript.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>javascript</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/promise.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>promise</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link is-active has-active" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/vue.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>vue</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/%E5%B7%A5%E7%A8%8B%E5%8C%96.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>工程化</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/%E6%B5%8F%E8%A7%88%E5%99%A8.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>浏览器</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-983f6b21 data-v-983f6b21><div class="item" data-v-983f6b21><div class="indicator" data-v-983f6b21></div><a class="VPLink link link" href="/interview/%E7%BD%91%E7%BB%9C.html" data-v-983f6b21 data-v-a8b5c975><!--[--><p class="text" data-v-983f6b21>网络</p><!--]--><!----></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-c3073c68 data-v-4e6b5525><div class="VPDoc has-sidebar has-aside" data-v-4e6b5525 data-v-ae7d01fc><div class="container" data-v-ae7d01fc><div class="aside" data-v-ae7d01fc><div class="aside-curtain" data-v-ae7d01fc></div><div class="aside-container" data-v-ae7d01fc><div class="aside-content" data-v-ae7d01fc><div class="VPDocAside" data-v-ae7d01fc data-v-cdc66372><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline has-outline" data-v-cdc66372 data-v-4414ac5c><div class="content" data-v-4414ac5c><div class="outline-marker" data-v-4414ac5c></div><div class="outline-title" data-v-4414ac5c>On this page</div><nav aria-labelledby="doc-outline-aria-label" data-v-4414ac5c><span class="visually-hidden" id="doc-outline-aria-label" data-v-4414ac5c> Table of Contents for current page </span><ul class="root" data-v-4414ac5c data-v-1188541a><!--[--><li data-v-1188541a><a class="outline-link" href="#_1-谈一谈对-mvvm-的理解" data-v-1188541a>1. 谈一谈对 MVVM 的理解？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_2-说一下-vue-的优点" data-v-1188541a>2. 说一下 Vue 的优点</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_3-解释一下对-vue-生命周期的理解" data-v-1188541a>3. 解释一下对 Vue 生命周期的理解</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_4-vue-实现双向数据绑定原理是什么" data-v-1188541a>4. Vue 实现双向数据绑定原理是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_5-说一下对-vue2-x-响应式原理的理解" data-v-1188541a>5. 说一下对 Vue2.x 响应式原理的理解</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_6-说一下在-vue2-x-中如何检测数组的变化" data-v-1188541a>6. 说一下在 Vue2.x 中如何检测数组的变化？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_7-vue3-x-响应式数据" data-v-1188541a>7. Vue3.x 响应式数据</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_8-v-model-双向绑定的原理是什么" data-v-1188541a>8. v-model 双向绑定的原理是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_9-vue2-x-和-vuex3-x-渲染器的-diff-算法分别说一下" data-v-1188541a>9. vue2.x 和 vuex3.x 渲染器的 diff 算法分别说一下？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_10-vue-组件的参数传递" data-v-1188541a>10. vue 组件的参数传递</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_11-vue-的路由实现" data-v-1188541a>11. Vue 的路由实现</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_12-vuex-是什么-怎么使用它-什么场景下我们会使用到-vuex" data-v-1188541a>12. vuex 是什么？怎么使用它？什么场景下我们会使用到 vuex</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_13-说一下-v-for-与-v-show-的区别" data-v-1188541a>13. 说一下 v-for 与 v-show 的区别</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_14-如何让-css-值在当前的组件中起作用" data-v-1188541a>14. 如何让 CSS 值在当前的组件中起作用</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_15-keep-alive-相关" data-v-1188541a>15. keep-alive 相关</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_16-vue-中如何进行组件的使用-vue-如何实现全局组件的注册" data-v-1188541a>16. Vue 中如何进行组件的使用？Vue 如何实现全局组件的注册？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_17-vue-cli-工程相关" data-v-1188541a>17. vue-cli 工程相关</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_18-nexttick-的作用是什么-他的实现原理是什么" data-v-1188541a>18. nextTick 的作用是什么？他的实现原理是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_19-说一下-vue-ssr-的实现原理" data-v-1188541a>19. 说一下 Vue SSR 的实现原理</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_20-vue-组件的-data-为什么必须是函数" data-v-1188541a>20. Vue 组件的 data 为什么必须是函数</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_21-说一下-vue-的-computed-的实现原理" data-v-1188541a>21. 说一下 Vue 的 computed 的实现原理</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_22-说一下-vue-complier-的实现原理是什么样的" data-v-1188541a>22. 说一下 Vue complier 的实现原理是什么样的？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_23-vue-如何快速定位那个组件出现性能问题的" data-v-1188541a>23. vue 如何快速定位那个组件出现性能问题的</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_24-proxy-相比-defineproperty-的优势在哪里" data-v-1188541a>24. Proxy 相比 defineProperty 的优势在哪里</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_25-vue-与-angular-以及-react-的区别是什么" data-v-1188541a>25. Vue 与 Angular 以及 React 的区别是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_26-说一下-watch-与-computed-的区别是什么-以及他们的使用场景分别是什么" data-v-1188541a>26. 说一下 watch 与 computed 的区别是什么？以及他们的使用场景分别是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_27-scoped-是如何实现样式穿透的" data-v-1188541a>27. scoped 是如何实现样式穿透的？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_28-说一下-ref-的作用是什么" data-v-1188541a>28. 说一下 ref 的作用是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_29-说一下你知道的-vue-修饰符都有哪些" data-v-1188541a>29. 说一下你知道的 vue 修饰符都有哪些？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_30-如何实现-vue-项目中的性能优化" data-v-1188541a>30. 如何实现 vue 项目中的性能优化？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_31-vue-extend-和-vue-component-的区别是什么" data-v-1188541a>31. Vue.extend 和 Vue.component 的区别是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_32-vue-中的-spa-应用如何优化首屏加载速度" data-v-1188541a>32. vue 中的 spa 应用如何优化首屏加载速度?</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_33-移动端如何实现一个比较友好的-header-组件" data-v-1188541a>33. 移动端如何实现一个比较友好的 header 组件</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_34-既然-vue-通过数据劫持可以精准探测数据变化-为什么还需要虚拟-dom-进行-diff-监测差异" data-v-1188541a>34. 既然 Vue 通过数据劫持可以精准探测数据变化，为什么还需要虚拟 DOM 进行 diff 监测差异 ？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_35-vue-为什么没有类似于-react-中-shouldcomponentupdate-的生命周期" data-v-1188541a>35. Vue 为什么没有类似于 React 中 shouldComponentUpdate 的生命周期？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_36-vue-中的-key-的作用是什么" data-v-1188541a>36. Vue 中的 Key 的作用是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_37-你的接口请求一般放在哪个生命周期中-为什么要这样做" data-v-1188541a>37. 你的接口请求一般放在哪个生命周期中？为什么要这样做？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_38-说一下你对-vue-事件绑定原理的理解" data-v-1188541a>38. 说一下你对 vue 事件绑定原理的理解？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_39-说一下-vue-模版编译的原理是什么" data-v-1188541a>39. 说一下 vue 模版编译的原理是什么</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_40-delete-和-vue-delete-删除数组的区别是什么" data-v-1188541a>40. delete 和 Vue.delete 删除数组的区别是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_41-v-on-可以实现监听多个方法么" data-v-1188541a>41. v-on 可以实现监听多个方法么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_42-vue-的数据为什么频繁变化但只会更新一次" data-v-1188541a>42. vue 的数据为什么频繁变化但只会更新一次？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_43-说一下-vue-中-computed-和-methods-的区别是什么" data-v-1188541a>43. 说一下 vue 中 computed 和 methods 的区别是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_44-在-vue-中要获取当前时间你会放到-computed-还是-methods-里-抖音直播" data-v-1188541a>44. 在 Vue 中要获取当前时间你会放到 computed 还是 methods 里？(抖音直播)</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_45-在给-vue-中的元素设置-key-值时可以使用-math-的-random-方法么" data-v-1188541a>45. 在给 vue 中的元素设置 key 值时可以使用 Math 的 random 方法么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_46-插槽与作用域插槽的区别是什么" data-v-1188541a>46. 插槽与作用域插槽的区别是什么？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_47-vue-中相同逻辑如何进行抽离" data-v-1188541a>47. vue 中相同逻辑如何进行抽离？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_48-如何监听-pushstate-和-replacestate-的变化呢" data-v-1188541a>48. 如何监听 pushstate 和 replacestate 的变化呢？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_49-说一下-vue3-0-是如何变得更快的" data-v-1188541a>49. 说一下 vue3.0 是如何变得更快的？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_50-说一说自定义指令有哪些生命周期" data-v-1188541a>50. 说一说自定义指令有哪些生命周期？</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_51-说一说相比-vue3-x-对比-vue2-x-变化" data-v-1188541a>51. 说一说相比 vue3.x 对比 vue2.x 变化</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_52-vue-为什么采用异步渲染" data-v-1188541a>52. vue 为什么采用异步渲染</a><!----></li><li data-v-1188541a><a class="outline-link" href="#_53-组件中写-name-选项有哪些好处" data-v-1188541a>53. 组件中写 name 选项有哪些好处</a><!----></li><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-cdc66372></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-ae7d01fc><div class="content-container" data-v-ae7d01fc><!--[--><!--]--><main class="main" data-v-ae7d01fc><div style="position:relative;" class="vp-doc _interview_vue" data-v-ae7d01fc><div><h2 id="_1-谈一谈对-mvvm-的理解" tabindex="-1">1. <strong>谈一谈对 <em>MVVM</em> 的理解？</strong> <a class="header-anchor" href="#_1-谈一谈对-mvvm-的理解" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><ul><li><em>MVVM</em> 是 <em>Model-View-ViewModel</em> 的缩写。<em>MVVM</em> 是一种设计思想。</li><li><em>Model</em> 层代表数据模型，也可以在 <em>Model</em> 中定义数据修改和操作的业务逻辑;</li><li><em>View</em> 代表 <em>UI</em> 组件，它负责将数据模型转化成 <em>UI</em> 展现出来，<em>View</em> 是一个同步 <em>View</em> 和 <em>Model</em> 的对象</li><li>在 <em>MVVM</em> 架构下，<em>View</em> 和 <em>Model</em> 之间并没有直接的联系，而是通过 <em>ViewModel</em> 进行交互， <em>Model</em> 和 <em>ViewModel</em> 之间的交互是双向的， 因此 <em>View</em> 数据的变化会同步到 <em>Model</em> 中，而 <em>Model</em> 数据的变化也会立即反应到 <em>View</em> 上。</li><li>对 <em>ViewModel</em> 通过双向数据绑定把 <em>View</em> 层和 <em>Model</em> 层连接了起来，而 <em>View</em> 和 <em>Model</em> 之间的 同步工作完全是自动的，无需人为干涉，因此开发者只需关注业务逻辑，不需要手动操作 <em>DOM</em>，不需要关注数据状态的同步问题，复杂的数据状态维护完全由 <em>MVVM</em> 来统一管理。</li></ul></blockquote><h2 id="_2-说一下-vue-的优点" tabindex="-1">2. <strong>说一下 <em>Vue</em> 的优点</strong> <a class="header-anchor" href="#_2-说一下-vue-的优点" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>Vue</em> 是一个构建数据驱动的 <em>Web</em> 界面的渐进式框架。</p><p><em>Vue</em> 的目标是通过尽可能简单的 <em>API</em> 实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。</p><p>关于 <em>Vue</em> 的优点，主要有<strong>响应式编程、组件化开发、虚拟 <em>DOM</em></strong></p><p><strong>响应式编程</strong></p><p>这里的响应式不是 <em>@media</em> 媒体查询中的响应式布局，而是指 <em>Vue</em> 会自动对页面中某些数据的变化做出响应。这也就是 <em>Vue</em> 最大的优点，通过 <em>MVVM</em> 思想实现数据的双向绑定，让开发者不用再操作 <em>DOM</em> 对象，有更多的时间去思考业务逻辑。</p><p><strong>组件化开发</strong></p><p><em>Vue</em> 通过组件，把一个单页应用中的各种模块拆分到一个一个单独的组件（<em>component</em>）中，我们只要先在父级应用中写好各种组件标签（占坑），并且在组件标签中写好要传入组件的参数（就像给函数传入参数一样，这个参数叫做组件的属性），然后再分别写好各种组件的实现（填坑），然后整个应用就算做完了。</p><p>组件化开发的优点：提高开发效率、方便重复使用、简化调试步骤、提升整个项目的可维护性、便于协同开发。</p><p><strong>虚拟 <em>DOM</em></strong></p><p>在传统开发中，用 <em>JQuery</em> 或者原生的 <em>JavaScript DOM</em> 操作函数对 <em>DOM</em> 进行频繁操作的时候，浏览器要不停的渲染新的 <em>DOM</em> 树，导致在性能上面的开销特别的高。</p><p>而 <em>Virtual DOM</em> 则是虚拟 <em>DOM</em> 的英文，简单来说，他就是一种可以预先通过 <em>JavaScript</em> 进行各种计算，把最终的 <em>DOM</em> 操作计算出来并优化，由于这个 <em>DOM</em> 操作属于预处理操作，并没有真实的操作 <em>DOM</em>，所以叫做虚拟 <em>DOM</em>。最后在计算完毕才真正将 <em>DOM</em> 操作提交，将 <em>DOM</em> 操作变化反映到 <em>DOM</em> 树上。</p></blockquote><h2 id="_3-解释一下对-vue-生命周期的理解" tabindex="-1">3. <strong>解释一下对 <em>Vue</em> 生命周期的理解</strong> <a class="header-anchor" href="#_3-解释一下对-vue-生命周期的理解" aria-hidden="true">#</a></h2><ul><li>什么是 <em>vue</em> 生命周期</li><li><em>vue</em> 生命周期的作用是什么</li><li><em>vue</em> 生命周期有几个阶段</li><li>第一次页面加载会触发哪几个钩子</li><li><em>DOM</em> 渲染在哪个周期就已经完成</li><li>多组件（父子组件）中生命周期的调用顺序说一下</li></ul><blockquote><p>参考答案：</p><p><strong>什么是 <em>vue</em> 生命周期</strong></p><p>对于 <em>vue</em> 来讲，生命周期就是一个 <em>vue</em> 实例从创建到销毁的过程。</p><p><strong><em>vue</em> 生命周期的作用是什么</strong></p><p>在生命周期的过程中会运行着一些叫做生命周期的函数，给予了开发者在不同的生命周期阶段添加业务代码的能力。</p><p>其实和回调是一个概念，当系统执行到某处时，检查是否有 <em>hook</em>(钩子)，有的话就会执行回调。</p><p>通俗的说，<em>hook</em> 就是在程序运行中，在某个特定的位置，框架的开发者设计好了一个钩子来告诉我们当前程序已经运行到特定的位置了，会触发一个回调函数，并提供给我们，让我们可以在生命周期的特定阶段进行相关业务代码的编写。</p><p><strong><em>vue</em> 生命周期有几个阶段</strong></p><p>它可以总共分为 <em>8</em> 个阶段：创建前/后, 载入前/后,更新前/后,销毁前/销毁后。</p><ul><li><p><em>beforeCreate</em>：是 <em>new Vue( )</em> 之后触发的第一个钩子，在当前阶段 <em>data、methods、computed</em> 以及 <em>watch</em> 上的数据和方法都不能被访问。</p></li><li><p><em>created</em>：在实例创建完成后发生，当前阶段已经完成了数据观测，也就是可以使用数据，更改数据，在这里更改数据不会触发 <em>updated</em> 函数。可以做一些初始数据的获取，在当前阶段无法与 <em>DOM</em> 进行交互，如果非要想，可以通过 <em>vm.$nextTick</em> 来访问 <em>DOM</em> 。</p></li><li><p><em>beforeMount</em>：发生在挂载之前，在这之前 <em>template</em> 模板已导入渲染函数编译。而当前阶段虚拟 <em>DOM</em> 已经创建完成，即将开始渲染。在此时也可以对数据进行更改，不会触发 <em>updated</em>。</p></li><li><p><em>mounted</em>：在挂载完成后发生，在当前阶段，真实的 <em>DOM</em> 挂载完毕，数据完成双向绑定，可以访问到 <em>DOM</em> 节点，使用 <em>$refs</em> 属性对 <em>DOM</em> 进行操作。</p></li><li><p><em>beforeUpdate</em>：发生在更新之前，也就是响应式数据发生更新，虚拟 <em>DOM</em> 重新渲染之前被触发，你可以在当前阶段进行更改数据，不会造成重渲染。</p></li><li><p><em>updated</em>：发生在更新完成之后，当前阶段组件 <em>DOM</em> 已完成更新。要注意的是避免在此期间更改数据，因为这可能会导致无限循环的更新。</p></li><li><p><em>beforeDestroy</em>：发生在实例销毁之前，在当前阶段实例完全可以被使用，我们可以在这时进行善后收尾工作，比如清除计时器。</p></li><li><p><em>destroyed</em>：发生在实例销毁之后，这个时候只剩下了 <em>DOM</em> 空壳。组件已被拆解，数据绑定被卸除，监听被移出，子实例也统统被销毁。</p></li></ul><p><strong>第一次页面加载会触发哪几个钩子</strong></p><p>会触发 <em>4</em> 个钩子，分别是：<em>beforeCreate、created、beforeMount、mounted</em></p><p><strong><em>DOM</em> 渲染在哪个周期就已经完成</strong></p><p><em>DOM</em> 渲染是在 <em>mounted</em> 阶段完成，此阶段真实的 <em>DOM</em> 挂载完毕，数据完成双向绑定，可以访问到 <em>DOM</em> 节点。</p><p><strong>多组件（父子组件）中生命周期的调用顺序说一下</strong></p><p>组件的调用顺序都是先父后子，渲染完成的顺序是先子后父。组件的销毁操作是先父后子，销毁完成的顺序是先子后父。</p><ul><li><p>加载渲染过程：父<em>beforeCreate</em>-&gt;父<em>created</em>-&gt;父<em>beforeMount</em>-&gt;子<em>beforeCreate</em>-&gt;子<em>created</em>-&gt;子<em>beforeMount</em>- &gt;子<em>mounted</em>-&gt;父<em>mounted</em></p></li><li><p>子组件更新过程：父<em>beforeUpdate</em>-&gt;子<em>beforeUpdate</em>-&gt;子<em>updated</em>-&gt;父<em>updated</em></p></li><li><p>父组件更新过程：父 <em>beforeUpdate</em> -&gt; 父 <em>updated</em></p></li><li><p>销毁过程：父<em>beforeDestroy</em>-&gt;子<em>beforeDestroy</em>-&gt;子<em>destroyed</em>-&gt;父<em>destroyed</em></p></li></ul></blockquote><h2 id="_4-vue-实现双向数据绑定原理是什么" tabindex="-1">4. <strong><em>Vue</em> 实现双向数据绑定原理是什么？</strong> <a class="header-anchor" href="#_4-vue-实现双向数据绑定原理是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>Vue2.x</em> 采用数据劫持结合发布订阅模式（<em>PubSub</em> 模式）的方式，通过 <em>Object.defineProperty</em> 来劫持各个属性的 <em>setter、getter</em>，在数据变动时发布消息给订阅者，触发相应的监听回调。</p><p>当把一个普通 <em>Javascript</em> 对象传给 <em>Vue</em> 实例来作为它的 <em>data</em> 选项时，<em>Vue</em> 将遍历它的属性，用 <em>Object.defineProperty</em> 将它们转为 <em>getter/setter</em>。用户看不到 <em>getter/setter</em>，但是在内部它们让 <em>Vue</em> 追踪依赖，在属性被访问和修改时通知变化。</p><p><em>Vue</em> 的数据双向绑定整合了 <em>Observer</em>，<em>Compile</em> 和 <em>Watcher</em> 三者，通过 <em>Observer</em> 来监听自己的 <em>model</em> 的数据变化，通过 <em>Compile</em> 来解析编译模板指令，最终利用 <em>Watcher</em> 搭起 <em>Observer</em> 和 <em>Compile</em> 之间的通信桥梁，达到数据变化-&gt;视图更新，视图交互变化（例如 input 操作）-&gt;数据 <em>model</em> 变更的双向绑定效果。</p><p><em>Vue3.x</em> 放弃了 <em>Object.defineProperty</em> ，使用 <em>ES6</em> 原生的 <em>Proxy</em>，来解决以前使用 <em>Object.defineProperty</em> 所存在的一些问题。</p></blockquote><h2 id="_5-说一下对-vue2-x-响应式原理的理解" tabindex="-1">5. <strong>说一下对 <em>Vue2.x</em> 响应式原理的理解</strong> <a class="header-anchor" href="#_5-说一下对-vue2-x-响应式原理的理解" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>Vue</em> 在初始化数据时，会使用 <em>Object.defineProperty</em> 重新定义 <em>data</em> 中的所有属性，当页面使用对应属性时，首先会进行依赖收集(收集当前组件的 <em>watcher</em>)，如果属性发生变化会通知相关依赖进行更新操作(发布订阅)。</p><p>（可以参阅前面第 <em>4</em> 题答案）</p></blockquote><h2 id="_6-说一下在-vue2-x-中如何检测数组的变化" tabindex="-1">6. <strong>说一下在 <em>Vue2.x</em> 中如何检测数组的变化？</strong> <a class="header-anchor" href="#_6-说一下在-vue2-x-中如何检测数组的变化" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>Vue2.x</em> 中实现检测数组变化的方法，是<strong>将数组的常用方法进行了重写</strong>。<em>Vue</em> 将 <em>data</em> 中的数组进行了原型链重写，指向了自己定义的数组原型方法。这样当调用数组 <em>api</em> 时，可以通知依赖更新。如果数组中包含着引用类型，会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。</p><p>流程:</p><ol><li>初始化传入 data 数据执行 initData</li><li>将数据进行观测 new Observer</li><li>将数组原型方法指向重写的原型</li><li>深度观察数组中的引用类型</li></ol><p>有两种情况无法检测到数组的变化。</p><ul><li>当利用索引直接设置一个数组项时，例如 <em>vm.items[indexOfItem] = newValue</em></li><li>当修改数组的长度时，例如 <em>vm.items.length = newLength</em></li></ul><p>不过这两种场景都有对应的解决方案。</p><p><strong>利用索引设置数组项的替代方案</strong></p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">//使用该方法进行更新视图</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// vm.$set，Vue.set的一个别名</span></span>
<span class="line"><span style="color:#A6ACCD;">vm</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">$set</span><span style="color:#A6ACCD;">(vm</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">items</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> indexOfItem</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> newValue)</span></span>
<span class="line"></span></code></pre></div><p><strong>修改数组的长度的替代方案</strong></p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">//使用该方法进行更新视图</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// Array.prototype.splice</span></span>
<span class="line"><span style="color:#A6ACCD;">vm</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">items</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">splice</span><span style="color:#A6ACCD;">(indexOfItem</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> newValue)</span></span>
<span class="line"></span></code></pre></div></blockquote><h2 id="_7-vue3-x-响应式数据" tabindex="-1">7. <strong><em>Vue3.x</em> 响应式数据</strong> <a class="header-anchor" href="#_7-vue3-x-响应式数据" aria-hidden="true">#</a></h2><ul><li><em>Vue3.x</em> 响应式数据原理是什么？</li><li><em>Proxy</em> 只会代理对象的第一层，那么 <em>Vue3</em> 又是怎样处理这个问题的呢？</li><li>监测数组的时候可能触发多次 <em>get/set</em>，那么如何防止触发多次呢？</li></ul><blockquote><p>参考答案：</p><p><strong><em>Vue3.x</em> 响应式数据原理是什么？</strong></p><p>在 <em>Vue 2</em> 中，响应式原理就是使用的 <em>Object.defineProperty</em> 来实现的。但是在 <em>Vue 3.0</em> 中采用了 <em>Proxy</em>，抛弃了 <em>Object.defineProperty</em> 方法。</p><p>究其原因，主要是以下几点：</p><ul><li><em>Object.defineProperty</em> 无法监控到数组下标的变化，导致通过数组下标添加元素，不能实时响应</li><li><em>Object.defineProperty</em> 只能劫持对象的属性，从而需要对每个对象，每个属性进行遍历，如果，属性值是对象，还需要深度遍历。<em>Proxy</em> 可以劫持整个对象，并返回一个新的对象。</li><li><em>Proxy</em> 不仅可以代理对象，还可以代理数组。还可以代理动态增加的属性。</li><li><em>Proxy</em> 有多达 <em>13</em> 种拦截方法</li><li><em>Proxy</em>作为新标准将受到浏览器厂商重点持续的性能优化</li></ul><p><strong><em>Proxy</em> 只会代理对象的第一层，那么 <em>Vue3</em> 又是怎样处理这个问题的呢？</strong></p><p>判断当前 <em>Reflect.get</em> 的返回值是否为 <em>Object</em>，如果是则再通过 <em>reactive</em> 方法做代理， 这样就实现了深度观测。</p><p><strong>监测数组的时候可能触发多次 <em>get/set</em>，那么如何防止触发多次呢？</strong></p><p>我们可以判断 <em>key</em> 是否为当前被代理对象 <em>target</em> 自身属性，也可以判断旧值与新值是否相等，只有满足以上两个条件之一时，才有可能执行 <em>trigger</em>。</p></blockquote><h2 id="_8-v-model-双向绑定的原理是什么" tabindex="-1">8. <strong><em>v-model</em> 双向绑定的原理是什么？</strong> <a class="header-anchor" href="#_8-v-model-双向绑定的原理是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>v-model</em> 本质就是 <em>:value + input</em> 方法的语法糖。可以通过 <em>model</em> 属性的 <em>prop</em> 和 <em>event</em> 属性来进行自定义。原生的 <em>v-model</em>，会根据标签的不同生成不同的事件和属性。</p><p>例如：</p><ul><li><em>text</em> 和 <em>textarea</em> 元素使用 <em>value</em> 属性和 <em>input</em> 事件</li><li><em>checkbox</em> 和 <em>radio</em> 使用 <em>checked</em> 属性和 <em>change</em> 事件</li><li><em>select</em> 字段将 <em>value</em> 作为 <em>prop</em> 并将 <em>change</em> 作为事件</li></ul><p>以输入框为例，当用户在输入框输入内容时，会触发 <em>input</em> 事件，从而更新 <em>value</em>。而 <em>value</em> 的改变同样会更新视图，这就是 <em>vue</em> 中的双向绑定。双向绑定的原理，其实现思路如下：</p><p>首先要对数据进行劫持监听，所以我们需要设置一个监听器 <em>Observer</em>，用来监听所有属性。如果属性发上变化了，就需要告诉订阅者 <em>Watcher</em> 看是否需要更新。</p><p>因为订阅者是有很多个，所以我们需要有一个消息订阅器 <em>Dep</em> 来专门收集这些订阅者，然后在监听器 <em>Observer</em> 和订阅者 <em>Watcher</em> 之间进行统一管理的。</p><p>接着，我们还需要有一个指令解析器 <em>Compile</em>，对每个节点元素进行扫描和解析，将相关指令对应初始化成一个订阅者 <em>Watcher</em>，并替换模板数据或者绑定相应的函数，此时当订阅者 <em>Watcher</em> 接收到相应属性的变化，就会执行对应的更新函数，从而更新视图。</p><p>因此接下去我们执行以下 <em>3</em> 个步骤，实现数据的双向绑定：</p><ol><li><p>实现一个监听器 <em>Observer</em>，用来劫持并监听所有属性，如果有变动的，就通知订阅者。</p></li><li><p>实现一个订阅者 <em>Watcher</em>，可以收到属性的变化通知并执行相应的函数，从而更新视图。</p></li><li><p>实现一个解析器 <em>Compile</em>，可以扫描和解析每个节点的相关指令，并根据初始化模板数据以及初始化相应的订阅器。</p></li></ol><p>流程图如下：</p><img src="https://img-blog.csdnimg.cn/img_convert/717034f25ee385b09e9dee53b2988cae.png" alt="img"></blockquote><h2 id="_9-vue2-x-和-vuex3-x-渲染器的-diff-算法分别说一下" tabindex="-1">9. <strong><em>vue2.x</em> 和 <em>vuex3.x</em> 渲染器的 <em>diff</em> 算法分别说一下？</strong> <a class="header-anchor" href="#_9-vue2-x-和-vuex3-x-渲染器的-diff-算法分别说一下" aria-hidden="true">#</a></h2><blockquote><p>直播讲解</p></blockquote><blockquote><p>参考答案：</p><p>简单来说，<em>diff</em> 算法有以下过程</p><ul><li>同级比较，再比较子节点</li><li>先判断一方有子节点一方没有子节点的情况(如果新的 <em>children</em> 没有子节点，将旧的子节点移除)</li><li>比较都有子节点的情况(核心 <em>diff</em>)</li><li>递归比较子节点</li></ul><p>正常 <em>Diff</em> 两个树的时间复杂度是 <em>O(n^3)</em>，但实际情况下我们很少会进行跨层级的移动 <em>DOM</em>，所以 <em>Vue</em> 将 <em>Diff</em> 进行了优化，从<em>O(n^3) -&gt; O(n)</em>，只有当新旧 <em>children</em> 都为多个子节点时才需要用核心的 <em>Diff</em> 算法进行同层级比较。</p><p><em>Vue2</em> 的核心 <em>Diff</em> 算法采用了双端比较的算法，同时从新旧 <em>children</em> 的两端开始进行比较，借助 <em>key</em> 值找到可复用的节点，再进行相关操作。相比 <em>React</em> 的 <em>Diff</em> 算法，同样情况下可以减少移动节点次数，减少不必要的性能损耗，更加的优雅。</p><p><em>Vue3.x</em> 借鉴了 <em>ivi</em> 算法和 <em>inferno</em> 算法</p><p>在创建 <em>VNode</em> 时就确定其类型，以及在 <em>mount/patch</em> 的过程中采用位运算来判断一个 <em>VNode</em> 的类型，在这个基础之上再配合核心的 <em>Diff</em> 算法，使得性能上较 <em>Vue2.x</em> 有了提升。该算法中还运用了动态规划的思想求解最长递归子序列。</p></blockquote><h2 id="_10-vue-组件的参数传递" tabindex="-1">10. <strong><em>vue</em> 组件的参数传递</strong> <a class="header-anchor" href="#_10-vue-组件的参数传递" aria-hidden="true">#</a></h2><ul><li>解释一下父组件与子组件传值实现过程</li><li>非父子组件的数据传递，兄弟组件传值是如何实现的</li></ul><blockquote><p>参考答案：</p><p><strong>解释一下父组件与子组件传值实现过程</strong></p><ul><li><p>父组件传给子组件：子组件通过 <em>props</em> 方法接受数据</p></li><li><p>子组件传给父组件：使用自定义事件，自组件通过 <em>$emit</em> 方法触发父组件的方法来传递参数</p></li></ul><p><strong>非父子组件的数据传递，兄弟组件传值是如何实现的</strong></p><p><em>eventBus</em>，就是创建一个事件中心，相当于中转站，可以用它来传递事件和接收事件。项目比较小时，用这个比较合适。</p><p>此外，总结 <em>vue</em> 中的组件通信方式，常见使用场景可以分为三类：</p><ul><li>父子通信： <ul><li>父向子传递数据是通过 <em>props</em> ，子向父是通过 <em>$emit / $on</em></li><li><em>$emit / $bus</em></li><li><em>vuex</em></li><li>通过父链 / 子链也可以通信（ <em>$parent / $children</em> ）</li><li><em>ref</em> 也可以访问组件实例</li><li><em>v-model</em></li><li>.<em>sync</em> 修饰符</li></ul></li><li>兄弟通信： <ul><li><em>$emit / $bus</em></li><li><em>vuex</em></li></ul></li><li>跨级通信： <ul><li><em>$emit / $bus</em></li><li><em>vuex</em></li><li><em>provide / inject API</em></li><li><em>$attrs/$listeners</em></li></ul></li></ul></blockquote><h2 id="_11-vue-的路由实现" tabindex="-1">11. <strong><em>Vue</em> 的路由实现</strong> <a class="header-anchor" href="#_11-vue-的路由实现" aria-hidden="true">#</a></h2><ul><li>解释 <em>hash</em> 模式和 <em>history</em> 模式的实现原理</li><li>说一下 <em>$router</em> 与 <em>$route</em> 的区别</li><li><em>vueRouter</em> 有哪几种导航守卫？</li><li>解释一下 <em>vueRouter</em> 的完整的导航解析流程是什么</li></ul><blockquote><p>参考答案：</p><p><strong>解释 <em>hash</em> 模式和 <em>history</em> 模式的实现原理</strong></p><p><code>#</code> 后面 <em>hash</em> 值的变化，不会导致浏览器向服务器发出请求，浏览器不发出请求，就不会刷新页面；通过监听 <em>hashchange</em> 事件可以知道 <em>hash</em> 发生了哪些变化，然后根据 <em>hash</em> 变化来实现更新页面部分内容的操作。</p><p><em>history</em> 模式的实现，主要是 <em>HTML5</em> 标准发布的两个 <em>API</em>，<em>pushState</em> 和 <em>replaceState</em>，这两个 <em>API</em> 可以在改变 <em>URL</em>，但是不会发送请求。这样就可以监听 <em>url</em> 变化来实现更新页面部分内容的操作。</p><p>两种模式的区别：</p><ul><li><p>首先是在 <em>URL</em> 的展示上，<em>hash</em> 模式有“#”，<em>history</em> 模式没有</p></li><li><p>刷新页面时，<em>hash</em> 模式可以正常加载到 <em>hash</em> 值对应的页面，而 <em>history</em> 没有处理的话，会返回 <em>404</em>，一般需要后端将所有页面都配置重定向到首页路由</p></li><li><p>在兼容性上，<em>hash</em> 可以支持低版本浏览器和 <em>IE</em></p></li></ul><p><strong>说一下 <em>$router</em> 与 <em>$route</em> 的区别</strong></p><p><em>$route</em> 对象表示当前的路由信息，包含了当前 <em>URL</em> 解析得到的信息。包含当前的路径，参数，<em>query</em> 对象等。</p><ul><li><em>$route.path</em>：字符串，对应当前路由的路径，总是解析为绝对路径，如 &quot;/foo/bar&quot;。</li><li><em>$route.params</em>： 一个 key/value 对象，包含了 动态片段 和 全匹配片段，如果没有路由参数，就是一个空对象。</li><li><em>$route.query</em>：一个 key/value 对象，表示 URL 查询参数。例如对于路径 <em>/foo?user=1</em>，则有 <em>$route.query.user == 1</em>，如果没有查询参数，则是个空对象。</li><li><em>$route.hash</em>：当前路由的 hash 值 (不带 #) ，如果没有 <em>hash</em> 值，则为空字符串。</li><li><em>$route.fullPath</em>：完成解析后的 <em>URL</em>，包含查询参数和 <em>hash</em> 的完整路径。</li><li><em>$route.matched</em>：数组，包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。</li><li><em>$route.name</em>：当前路径名字</li><li><em>$route.meta</em>：路由元信息</li></ul><p><em>$route</em> 对象出现在多个地方:</p><ul><li>组件内的 <em>this.$route</em> 和 <em>route watcher</em> 回调（监测变化处理）</li><li><em>router.match(location)</em> 的返回值</li><li><em>scrollBehavior</em> 方法的参数</li><li>导航钩子的参数，例如 <em>router.beforeEach</em> 导航守卫的钩子函数中，<em>to</em> 和 <em>from</em> 都是这个路由信息对象。</li></ul><p><em>$router</em> 对象是全局路由的实例，是 <em>router</em> 构造方法的实例。</p><p><em>$router</em> 对象常用的方法有：</p><ul><li><em>push</em>：向 <em>history</em> 栈添加一个新的记录</li><li><em>go</em>：页面路由跳转前进或者后退</li><li><em>replace</em>：替换当前的页面，不会向 <em>history</em> 栈添加一个新的记录</li></ul><p><strong><em>vueRouter</em> 有哪几种导航守卫？</strong></p><ul><li><p>全局前置/钩子：<em>beforeEach、beforeR-esolve、afterEach</em></p></li><li><p>路由独享的守卫：<em>beforeEnter</em></p></li><li><p>组件内的守卫：<em>beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave</em></p></li></ul><p><strong>解释一下 <em>vueRouter</em> 的完整的导航解析流程是什么</strong></p><p>一次完整的导航解析流程如下：</p><ol><li>导航被触发。</li><li>在失活的组件里调用离开守卫。</li><li>调用全局的 <em>beforeEach</em> 守卫。</li><li>在重用的组件里调用 <em>beforeRouteUpdate</em> 守卫（<em>2.2+</em>）。</li><li>在路由配置里调用 <em>beforeEnter</em>。</li><li>解析异步路由组件。</li><li>在被激活的组件里调用 <em>beforeRouteEnter</em>。</li><li>调用全局的 <em>beforeResolve</em> 守卫（<em>2.5+</em>）。</li><li>导航被确认。</li><li>调用全局的 <em>afterEach</em> 钩子。</li><li>触发 <em>DOM</em> 更新。</li><li>用创建好的实例调用 <em>beforeRouteEnter</em> 守卫中传给 <em>next</em> 的回调函数。</li></ol></blockquote><h2 id="_12-vuex-是什么-怎么使用它-什么场景下我们会使用到-vuex" tabindex="-1">12. <strong><em>vuex</em> 是什么？怎么使用它？什么场景下我们会使用到 <em>vuex</em></strong> <a class="header-anchor" href="#_12-vuex-是什么-怎么使用它-什么场景下我们会使用到-vuex" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><strong><em>vuex</em> 是什么</strong></p><p><em>vuex</em> 是一个专为 <em>Vue</em> 应用程序开发的状态管理器，采用集中式存储管理应用的所有组件的状态。每一个 <em>vuex</em> 应用的核心就是 <em>store</em>（仓库）。“<em>store</em>” 基本上就是一个容器，它包含着应用中大部分的状态 (<em>state</em>)。</p><p><strong>为什么需要 <em>vuex</em></strong></p><p>由于组件只维护自身的状态(<em>data</em>)，组件创建时或者路由切换时，组件会被初始化，从而导致 <em>data</em> 也随之销毁。</p><p><strong>使用方法</strong></p><p>在 <em>main.js</em> 引入 <em>store</em>，注入。只用来读取的状态集中放在 <em>store</em> 中， 改变状态的方式是提交 <em>mutations</em>，这是个同步的事物，异步逻辑应该封装在 <em>action</em> 中。</p><p><strong>什么场景下会使用到 <em>vuex</em></strong></p><p>如果是 <em>vue</em> 的小型应用，那么没有必要使用 <em>vuex</em>，这个时候使用 <em>vuex</em> 反而会带来负担。组件之间的状态传递使用 <em>props</em>、自定义事件来传递即可。</p><p>但是如果涉及到 <em>vue</em> 的大型应用，那么就需要类似于 <em>vuex</em> 这样的集中管理状态的状态机来管理所有组件的状态。例如登录状态、加入购物车、音乐播放等，总之只要是开发 <em>vue</em> 的大型应用，都推荐使用 <em>vuex</em> 来管理所有组件状态。</p></blockquote><h2 id="_13-说一下-v-for-与-v-show-的区别" tabindex="-1">13. <strong>说一下 <em>v-for</em> 与 <em>v-show</em> 的区别</strong> <a class="header-anchor" href="#_13-说一下-v-for-与-v-show-的区别" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><ul><li><p>共同点：都是动态显示 <em>DOM</em> 元素</p></li><li><p>区别点:</p><ul><li><p>手段</p><p><em>v-if</em> 是动态的向 <em>DOM</em> 树内添加或者删除 <em>DOM</em> 元素</p><p><em>v-show</em> 是通过设置 <em>DOM</em> 元素的 <em>display</em> 样式属性控制显隐</p></li><li><p>编译过程</p><p><em>v-if</em> 切换有一个局部编译/卸载的过程，切换过程中合适地销毁和重建内部的事件监听和子组件</p><p><em>v-show</em> 只是简单的基于 <em>css</em> 切换</p></li><li><p>编译条件</p><p><em>v-if</em> 是惰性的，如果初始条件为假，则什么也不做。只有在条件第一次变为真时才开始局部编译</p><p><em>v-show</em> 是在任何条件下(首次条件是否为真)都被编译，然后被缓存，而且 <em>DOM</em> 元素保留</p></li><li><p>性能消耗</p><p><em>v-if</em> 有更高的切换消耗</p><p><em>v-show</em> 有更高的初始渲染消耗</p></li><li><p>使用场景</p><p><em>v-if</em> 适合运营条件不大可能改变</p><p><em>v-show</em> 适合频繁切换</p></li></ul></li></ul></blockquote><h2 id="_14-如何让-css-值在当前的组件中起作用" tabindex="-1">14. <strong>如何让 <em>CSS</em> 值在当前的组件中起作用</strong> <a class="header-anchor" href="#_14-如何让-css-值在当前的组件中起作用" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>在 <em>vue</em> 文件中的 <em>style</em> 标签上，有一个特殊的属性：<em>scoped</em>。当一个 style 标签拥有 <em>scoped</em> 属性时，它的 <em>CSS</em> 样式就只能作用于当前的组件，也就是说，该样式只能适用于当前组件元素。通过该属性，可以使得组件之间的样式不互相污染。如果一个项目中的所有 <em>style</em> 标签全部加上了 <em>scoped</em>，相当于实现了样式的模块化。</p><p><strong><em>scoped</em> 的实现原理</strong></p><p><em>vue</em> 中的 <em>scoped</em> 属性的效果主要通过 <em>PostCSS</em> 转译实现的。<em>PostCSS</em> 给一个组件中的所有 <em>DOM</em> 添加了一个独一无二的动态属性，然后，给 <em>CSS</em> 选择器额外添加一个对应的属性选择器来选择该组件中 <em>DOM</em>，这种做法使得样式只作用于含有该属性的 <em>DOM</em>，即组件内部 <em>DOM</em>。</p><p>例如：</p><p>转译前</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">class</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">example</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">hi</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">scoped</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">.example </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  color: red;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span></code></pre></div><p>转译后：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">class</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">example</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">data-v-5558831a</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">hi</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">.example[data-v-5558831a] </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  color: red;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span></code></pre></div></blockquote><h2 id="_15-keep-alive-相关" tabindex="-1">15. <strong><em>keep-alive</em> 相关</strong> <a class="header-anchor" href="#_15-keep-alive-相关" aria-hidden="true">#</a></h2><ul><li>keep-alive的实现原理是什么</li><li>与keep-alive相关的生命周期函数是什么，什么场景下会进行使用</li><li>keep-alive的常用属性有哪些</li></ul><blockquote><p>参考答案：</p><p>keep-alive 组件是 vue 的内置组件，用于缓存内部组件实例。这样做的目的在于，keep-alive 内部的组件切回时，不用重新创建组件实例，而直接使用缓存中的实例，一方面能够避免创建组件带来的开销，另一方面可以保留组件的状态。</p><p>keep-alive 具有 include 和 exclude 属性，通过它们可以控制哪些组件进入缓存。另外它还提供了 max 属性，通过它可以设置最大缓存数，当缓存的实例超过该数时，vue 会移除最久没有使用的组件缓存。</p><p>受keep-alive的影响，其内部所有嵌套的组件都具有两个生命周期钩子函数，分别是 activated 和 deactivated，它们分别在组件激活和失活时触发。第一次 activated 触发是在 mounted 之后</p><p>在具体的实现上，keep-alive 在内部维护了一个 key 数组和一个缓存对象</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// keep-alive 内部的声明周期函数</span></span>
<span class="line"><span style="color:#82AAFF;">created</span><span style="color:#A6ACCD;"> () </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">cache</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">Object</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">create</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">null</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">keys</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> []</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div><p>key 数组记录目前缓存的组件 key 值，如果组件没有指定 key 值，则会为其自动生成一个唯一的 key 值</p><p>cache 对象以 key 值为键，vnode 为值，用于缓存组件对应的虚拟 DOM</p><p>在 keep-alive 的渲染函数中，其基本逻辑是判断当前渲染的 vnode 是否有对应的缓存，如果有，从缓存中读取到对应的组件实例；如果没有则将其缓存。</p><p>当缓存数量超过 max 数值时，keep-alive 会移除掉 key 数组的第一个元素。</p></blockquote><h2 id="_16-vue-中如何进行组件的使用-vue-如何实现全局组件的注册" tabindex="-1">16. <strong><em>Vue</em> 中如何进行组件的使用？<em>Vue</em> 如何实现全局组件的注册？</strong> <a class="header-anchor" href="#_16-vue-中如何进行组件的使用-vue-如何实现全局组件的注册" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>要使用组件，首先需要使用 <em>import</em> 来引入组件，然后在 <em>components</em> 属性中注册组件，之后就可以在模板中使用组件了。</p><p>可以使用 <em>Vue.component</em> 方法来实现全局组件的注册。</p></blockquote><h2 id="_17-vue-cli-工程相关" tabindex="-1">17. <strong><em>vue-cli</em> 工程相关</strong> <a class="header-anchor" href="#_17-vue-cli-工程相关" aria-hidden="true">#</a></h2><ul><li>构建 <em>vue-cli</em> 工程都用到了哪些技术？他们的作用分别是什么？</li><li><em>vue-cli</em> 工程常用的 <em>npm</em> 命令有哪些？</li></ul><blockquote><p>参考答案：</p><p><strong>构建 <em>vue-cli</em> 工程都用到了哪些技术？他们的作用分别是什么？</strong></p><ol><li>vue.js：vue-cli 工程的核心，主要特点是双向数据绑定和组件系统。</li><li>vue-router：vue 官方推荐使用的路由框架。</li><li>vuex：专为 Vue.js 应用项目开发的状态管理器，主要用于维护 vue 组件间共用的一些 变量 和 方法。</li><li>axios（或者 fetch、ajax）：用于发起 GET 、或 POST 等 http请求，基于 Promise 设计。</li><li>vux等：一个专为vue设计的移动端UI组件库。</li><li>webpack：模块加载和vue-cli工程打包器。</li><li>eslint：代码规范工具</li></ol><p><strong><em>vue-cli</em> 工程常用的 <em>npm</em> 命令有哪些？</strong></p><p>下载 node_modules 资源包的命令：npm install</p><p>启动 vue-cli 开发环境的 npm命令：npm run dev</p><p>vue-cli 生成 生产环境部署资源 的 npm命令：npm run build</p><p>用于查看 vue-cli 生产环境部署资源文件大小的 npm命令：npm run build --report</p></blockquote><h2 id="_18-nexttick-的作用是什么-他的实现原理是什么" tabindex="-1">18. <strong><em>nextTick</em> 的作用是什么？他的实现原理是什么？</strong> <a class="header-anchor" href="#_18-nexttick-的作用是什么-他的实现原理是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>作用：<em>vue</em> 更新 <em>DOM</em> 是异步更新的，数据变化，<em>DOM</em> 的更新不会马上完成，<em>nextTick</em> 的回调是在下次 <em>DOM</em> 更新循环结束之后执行的延迟回调。</p><p>实现原理：<em>nextTick</em> 主要使用了宏任务和微任务。根据执行环境分别尝试采用</p><ul><li><em>Promise</em>：可以将函数延迟到当前函数调用栈最末端</li><li><em>MutationObserver</em> ：是 <em>H5</em> 新加的一个功能，其功能是监听 <em>DOM</em> 节点的变动，在所有 <em>DOM</em> 变动完成后，执行回调函数</li><li><em>setImmediate</em>：用于中断长时间运行的操作，并在浏览器完成其他操作（如事件和显示更新）后立即运行回调函数</li><li>如果以上都不行则采用 <em>setTimeout</em> 把函数延迟到 DOM 更新之后再使用</li></ul><p>原因是宏任务消耗大于微任务，优先使用微任务，最后使用消耗最大的宏任务。</p></blockquote><h2 id="_19-说一下-vue-ssr-的实现原理" tabindex="-1">19. <strong>说一下 <em>Vue SSR</em> 的实现原理</strong> <a class="header-anchor" href="#_19-说一下-vue-ssr-的实现原理" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><ul><li><em>app.js</em> 作为客户端与服务端的公用入口，导出 <em>Vue</em> 根实例，供客户端 <em>entry</em> 与服务端 <em>entry</em> 使用。客户端 <em>entry</em> 主要作用挂载到 <em>DOM</em> 上，服务端 <em>entry</em> 除了创建和返回实例，还需要进行路由匹配与数据预获取。</li><li><em>webpack</em> 为客服端打包一个 <em>ClientBundle</em>，为服务端打包一个 <em>ServerBundle</em>。</li><li>服务器接收请求时，会根据 <em>url</em>，加载相应组件，获取和解析异步数据，创建一个读取 <em>Server Bundle</em> 的 <em>BundleRenderer</em>，然后生成 <em>html</em> 发送给客户端。</li><li>客户端混合，客户端收到从服务端传来的 <em>DOM</em> 与自己的生成的 <em>DOM</em> 进行对比，把不相同的 <em>DOM</em> 激活，使其可以能够响应后续变化，这个过程称为客户端激活（也就是转换为单页应用）。为确保混合成功，客户 端与服务器端需要共享同一套数据。在服务端，可以在渲染之前获取数据，填充到 <em>store</em> 里，这样，在客户端挂载到 <em>DOM</em> 之前，可以直接从 <em>store</em> 里取数据。首屏的动态数据通过 <em>window._<em>INITIAL_STATE</em>_</em> 发送到客户端</li><li><em>VueSSR</em> 的原理，主要就是通过 <em>vue-server-renderer</em> 把 <em>Vue</em> 的组件输出成一个完整 <em>HTML</em>，输出到客户端，到达客户端后重新展开为一个单页应用。</li></ul></blockquote><h2 id="_20-vue-组件的-data-为什么必须是函数" tabindex="-1">20. <strong><em>Vue</em> 组件的 <em>data</em> 为什么必须是函数</strong> <a class="header-anchor" href="#_20-vue-组件的-data-为什么必须是函数" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>组件中的 <em>data</em> 写成一个函数，数据以函数返回值形式定义。这样每复用一次组件，就会返回一份新的 <em>data</em>，类似于给每个组件实例创建一个私有的数据空间，让各个组件实例维护各自的数据。而单纯的写成对象形式，就使得所有组件实例共用了一份 <em>data</em>，就会造成一个变了全都会变的结果。</p></blockquote><h2 id="_21-说一下-vue-的-computed-的实现原理" tabindex="-1">21. <strong>说一下 <em>Vue</em> 的 <em>computed</em> 的实现原理</strong> <a class="header-anchor" href="#_21-说一下-vue-的-computed-的实现原理" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>当组件实例触发生命周期函数 <em>beforeCreate</em> 后，它会做一系列事情，其中就包括对 <em>computed</em> 的处理。</p><p>它会遍历 <em>computed</em> 配置中的所有属性，为每一个属性创建一个 <em>Watcher</em> 对象，并传入一个函数，该函数的本质其实就是 <em>computed</em> 配置中的 <em>getter</em>，这样一来，<em>getter</em> 运行过程中就会收集依赖</p><p>但是和渲染函数不同，为计算属性创建的 <em>Watcher</em> 不会立即执行，因为要考虑到该计算属性是否会被渲染函数使用，如果没有使用，就不会得到执行。因此，在创建 <em>Watcher</em> 的时候，它使用了 <em>lazy</em> 配置，<em>lazy</em> 配置可以让 <em>Watcher</em> 不会立即执行。</p><p>收到 <em>lazy</em> 的影响，<em>Watcher</em> 内部会保存两个关键属性来实现缓存，一个是 <em>value</em>，一个是 <em>dirty</em></p><p><em>value</em> 属性用于保存 <em>Watcher</em> 运行的结果，受 <em>lazy</em> 的影响，该值在最开始是 <em>undefined</em></p><p><em>dirty</em> 属性用于指示当前的 <em>value</em> 是否已经过时了，即是否为脏值，受 <em>lazy</em> 的影响，该值在最开始是 <em>true</em></p><p>Watcher 创建好后，vue 会使用代理模式，将计算属性挂载到组件实例中</p><p>当读取计算属性时，<em>vue</em> 检查其对应的 <em>Watcher</em> 是否是脏值，如果是，则运行函数，计算依赖，并得到对应的值，保存在 <em>Watcher</em> 的 <em>value</em> 中，然后设置 <em>dirty</em> 为 <em>false</em>，然后返回。</p><p>如果 <em>dirty</em> 为 <em>false</em>，则直接返回 <em>watcher</em> 的 <em>value</em></p><p>巧妙的是，在依赖收集时，被依赖的数据不仅会收集到计算属性的 <em>Watcher</em>，还会收集到组件的 <em>Watcher</em></p><p>当计算属性的依赖变化时，会先触发计算属性的 <em>Watcher</em> 执行，此时，它只需设置 <em>dirty</em> 为 <em>true</em> 即可，不做任何处理。</p><p>由于依赖同时会收集到组件的 <em>Watcher</em>，因此组件会重新渲染，而重新渲染时又读取到了计算属性，由于计算属性目前已为 dirty，因此会重新运行 <em>getter</em> 进行运算</p><p>而对于计算属性的 <em>setter</em>，则极其简单，当设置计算属性时，直接运行 <em>setter</em> 即可。</p></blockquote><h2 id="_22-说一下-vue-complier-的实现原理是什么样的" tabindex="-1">22. <strong>说一下 <em>Vue complier</em> 的实现原理是什么样的？</strong> <a class="header-anchor" href="#_22-说一下-vue-complier-的实现原理是什么样的" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>在使用 vue 的时候，我们有两种方式来创建我们的 HTML 页面，第一种情况，也是大多情况下，我们会使用模板 template 的方式，因为这更易读易懂也是官方推荐的方法；第二种情况是使用 render 函数来生成 HTML，它比 template 更接近最终结果。</p><p>complier 的主要作用是解析模板，生成渲染模板的 <em>render</em>， 而 <em>render</em> 的作用主要是为了生成 <em>VNode</em></p><p>complier 主要分为 3 大块：</p><ul><li>parse：接受 template 原始模板，按着模板的节点和数据生成对应的 ast</li><li>optimize：遍历 ast 的每一个节点，标记静态节点，这样就知道哪部分不会变化，于是在页面需要更新时，通过 diff 减少去对比这部分DOM，提升性能</li><li>generate 把前两步生成完善的 ast，组成 render 字符串，然后将 render 字符串通过 new Function 的方式转换成渲染函数</li></ul></blockquote><h2 id="_23-vue-如何快速定位那个组件出现性能问题的" tabindex="-1">23. <strong><em>vue</em> 如何快速定位那个组件出现性能问题的</strong> <a class="header-anchor" href="#_23-vue-如何快速定位那个组件出现性能问题的" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>⽤ <em>timeline</em> ⼯具。 通过 <em>timeline</em> 来查看每个函数的调⽤时常，定位出哪个函数的问题，从⽽能判断哪个组件出了问题。</p></blockquote><h2 id="_24-proxy-相比-defineproperty-的优势在哪里" tabindex="-1">24. <strong><em>Proxy</em> 相比 <em>defineProperty</em> 的优势在哪里</strong> <a class="header-anchor" href="#_24-proxy-相比-defineproperty-的优势在哪里" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>Vue3.x</em> 改用 <em>Proxy</em> 替代 <em>Object.defineProperty</em></p><p>原因在于 <em>Object.defineProperty</em> 本身存在的一些问题：</p><ul><li><em>Object.defineProperty</em> 只能劫持对象属性的 <em>getter</em> 和 <em>setter</em> 方法。</li><li><em>Object.definedProperty</em> 不支持数组(可以监听数组,不过数组方法无法监听自己重写)，更准确的说是不支持数组的各种 <em>API</em>(所以 <em>Vue</em> 重写了数组方法。</li></ul><p>而相比 <em>Object.defineProperty</em>，<em>Proxy</em> 的优点在于：</p><ul><li><em>Proxy</em> 是直接代理劫持整个对象。</li><li><em>Proxy</em> 可以直接监听对象和数组的变化，并且有多达 <em>13</em> 种拦截方法。</li></ul><p>目前，<em>Object.definedProperty</em> 唯一比 <em>Proxy</em> 好的一点就是兼容性，不过 <em>Proxy</em> 新标准也受到浏览器厂商重点持续的性能优化当中。</p></blockquote><h2 id="_25-vue-与-angular-以及-react-的区别是什么" tabindex="-1">25. <strong><em>Vue</em> 与 <em>Angular</em> 以及 <em>React</em> 的区别是什么？</strong> <a class="header-anchor" href="#_25-vue-与-angular-以及-react-的区别是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>这种题目是开放性题目，一般是面试过程中面试官口头来提问，不太可能出现在笔试试卷里面。</p><p>关于 <em>Vue</em> 和其他框架的不同，官方专门写了一篇文档，从性能、体积、灵活性等多个方面来进行了说明。</p><p>详细可以参阅：<em><a href="https://cn.vuejs.org/v2/guide/comparison.html" target="_blank" rel="noreferrer">https://cn.vuejs.org/v2/guide/comparison.html</a></em></p><p>建议面试前通读一遍该篇文档，然后进行适当的总结。</p></blockquote><h2 id="_26-说一下-watch-与-computed-的区别是什么-以及他们的使用场景分别是什么" tabindex="-1">26. <strong>说一下 <em>watch</em> 与 <em>computed</em> 的区别是什么？以及他们的使用场景分别是什么？</strong> <a class="header-anchor" href="#_26-说一下-watch-与-computed-的区别是什么-以及他们的使用场景分别是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>区别：</p><ol><li>都是观察数据变化的（相同）</li><li>计算属性将会混入到 vue 的实例中，所以需要监听自定义变量；watch 监听 data 、props 里面数据的变化；</li><li>computed 有缓存，它依赖的值变了才会重新计算，watch 没有；</li><li>watch 支持异步，computed 不支持；</li><li>watch 是一对多（监听某一个值变化，执行对应操作）；computed 是多对一（监听属性依赖于其他属性）</li><li>watch 监听函数接收两个参数，第一个是最新值，第二个是输入之前的值；</li><li>computed 属性是函数时，都有 get 和 set 方法，默认走 get 方法，get 必须有返回值（return）</li></ol><p>watch 的 参数：</p><ul><li>deep：深度监听</li><li>immediate ：组件加载立即触发回调函数执行</li></ul><p>computed 缓存原理：</p><p>conputed本质是一个惰性的观察者；当计算数据存在于 data 或者 props里时会被警告；</p><p>vue 初次运行会对 computed 属性做初始化处理（initComputed），初始化的时候会对每一个 computed 属性用 watcher 包装起来 ，这里面会生成一个 dirty 属性值为 true；然后执行 defineComputed 函数来计算，计算之后会将 dirty 值变为 false，这里会根据 dirty 值来判断是否需要重新计算；如果属性依赖的数据发生变化，computed 的 watcher 会把 dirty 变为 true，这样就会重新计算 computed 属性的值。</p></blockquote><h2 id="_27-scoped-是如何实现样式穿透的" tabindex="-1">27. <strong><em>scoped</em> 是如何实现样式穿透的？</strong> <a class="header-anchor" href="#_27-scoped-是如何实现样式穿透的" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>首先说一下什么场景下需要 <em>scoped</em> 样式穿透。</p><p>在很多项目中，会出现这么一种情况，即：引用了第三方组件，需要在组件中局部修改第三方组件的样式，而又不想去除 <em>scoped</em> 属性造成组件之间的样式污染。此时只能通过特殊的方式，穿透 <em>scoped</em>。</p><p>有三种常用的方法来实现样式穿透。</p><p><strong>方法一</strong></p><p>使用 <em>::v-deep</em> 操作符( &gt;&gt;&gt; 的别名)</p><p>如果希望 <em>scoped</em> 样式中的一个选择器能够作用得“更深”，例如影响子组件，可以使用 &gt;&gt;&gt; 操作符：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">scoped</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    .a &gt;&gt;&gt; .b </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;"> </span><span style="color:#676E95;font-style:italic;">/* ... */</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span></code></pre></div><p>上述代码将会编译成：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">a[data</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">v</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">f3f3eg9] </span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">b </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#676E95;font-style:italic;">/* ... */</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div><p>后面的类名没有 <em>data</em> 属性，所以能选到子组件里面的类名。</p><p>有些像 <em>Sass</em> 之类的预处理器无法正确解析 &gt;&gt;&gt;，所以需要使用 <em>::v-deep</em> 操作符来代替。</p><p><strong>方法二</strong></p><p>定义一个含有 <em>scoped</em> 属性的 <em>style</em> 标签之外，再定义一个不含有 <em>scoped</em> 属性的 <em>style</em> 标签，即在一个 <em>vue</em> 组件中定义一个全局的 <em>style</em> 标签，一个含有作用域的 <em>style</em> 标签：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">/* global styles */</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">scoped</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">/* local styles */</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span></code></pre></div><p>此时，我们只需要将修改第三方样式的 <em>css</em> 写在第一个 <em>style</em> 中即可。</p><p><strong>方法三</strong></p><p>上面的方法一需要单独书写一个不含有 <em>scoped</em> 属性的 <em>style</em> 标签，可能会造成全局样式的污染。</p><p>更推荐的方式是在组件的外层 <em>DOM</em> 上添加唯一的 <em>class</em> 来区分不同组件，在书写样式时就可以正常针对针对这部分 <em>DOM</em> 书写样式。</p></blockquote><h2 id="_28-说一下-ref-的作用是什么" tabindex="-1">28. <strong>说一下 <em>ref</em> 的作用是什么？</strong> <a class="header-anchor" href="#_28-说一下-ref-的作用是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>ref</em> 的作用是被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 <em>$refs</em> 对象上。其特点是：</p><ul><li>如果在普通的 <em>DOM</em> 元素上使用，引用指向的就是 <em>DOM</em> 元素</li><li>如果用在子组件上，引用就指向组件实例</li></ul><p>所以常见的使用场景有：</p><ol><li>基本用法，本页面获取 <em>DOM</em> 元素</li><li>获取子组件中的 <em>data</em></li><li>调用子组件中的方法</li></ol></blockquote><h2 id="_29-说一下你知道的-vue-修饰符都有哪些" tabindex="-1">29. <strong>说一下你知道的 <em>vue</em> 修饰符都有哪些？</strong> <a class="header-anchor" href="#_29-说一下你知道的-vue-修饰符都有哪些" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>在 <em>vue</em> 中修饰符可以分为 <em>3</em> 类：</p><ul><li>事件修饰符</li><li>按键修饰符</li><li>表单修饰符</li></ul><p><strong>事件修饰符</strong></p><p>在事件处理程序中调用 <em>event.preventDefault</em> 或 <em>event.stopPropagation</em> 方法是非常常见的需求。尽管可以在 <em>methods</em> 中轻松实现这点，但更好的方式是：<em>methods</em> 只有纯粹的数据逻辑，而不是去处理 <em>DOM</em> 事件细节。</p><p>为了解决这个问题，<em>vue</em> 为 <em>v-on</em> 提供了事件修饰符。通过由点 <em>.</em> 表示的指令后缀来调用修饰符。</p><p>常见的事件修饰符如下：</p><ul><li><em>.stop</em>：阻止冒泡。</li><li><em>.prevent</em>：阻止默认事件。</li><li><em>.capture</em>：使用事件捕获模式。</li><li><em>.self</em>：只在当前元素本身触发。</li><li><em>.once</em>：只触发一次。</li><li><em>.passive</em>：默认行为将会立即触发。</li></ul><p><strong>按键修饰符</strong></p><p>除了事件修饰符以外，在 <em>vue</em> 中还提供了有鼠标修饰符，键值修饰符，系统修饰符等功能。</p><ul><li>.<em>left</em>：左键</li><li>.<em>right</em>：右键</li><li>.<em>middle</em>：滚轮</li><li>.<em>enter</em>：回车</li><li>.<em>tab</em>：制表键</li><li>.<em>delete</em>：捕获 “删除” 和 “退格” 键</li><li>.<em>esc</em>：返回</li><li>.<em>space</em>：空格</li><li>.<em>up</em>：上</li><li>.<em>down</em>：下</li><li>.<em>left</em>：左</li><li>.<em>right</em>：右</li><li>.<em>ctrl</em>：<em>ctrl</em> 键</li><li>.<em>alt</em>：<em>alt</em> 键</li><li>.<em>shift</em>：<em>shift</em> 键</li><li>.<em>meta</em>：<em>meta</em> 键</li></ul><p><strong>表单修饰符</strong></p><p><em>vue</em> 同样也为表单控件也提供了修饰符，常见的有 <em>.lazy</em>、<em>.number</em> 和 <em>.trim</em>。</p><ul><li>.<em>lazy</em>：在文本框失去焦点时才会渲染</li><li>.<em>number</em>：将文本框中所输入的内容转换为number类型</li><li>.<em>trim</em>：可以自动过滤输入首尾的空格</li></ul></blockquote><h2 id="_30-如何实现-vue-项目中的性能优化" tabindex="-1">30. <strong>如何实现 <em>vue</em> 项目中的性能优化？</strong> <a class="header-anchor" href="#_30-如何实现-vue-项目中的性能优化" aria-hidden="true">#</a></h2><blockquote><p>直播课讲解</p></blockquote><blockquote><p>参考答案：</p><p><strong>编码阶段</strong></p><ul><li>尽量减少 <em>data</em> 中的数据，<em>data</em> 中的数据都会增加 <em>getter</em> 和 <em>setter</em>，会收集对应的 <em>watcher</em></li><li><em>v-if</em> 和 <em>v-for</em> 不能连用</li><li>如果需要使用 <em>v-for</em> 给每项元素绑定事件时使用事件代理</li><li><em>SPA</em> 页面采用 <em>keep-alive</em> 缓存组件</li><li>在更多的情况下，使用 <em>v-if</em> 替代 <em>v-show</em></li><li><em>key</em> 保证唯一</li><li>使用路由懒加载、异步组件</li><li>防抖、节流</li><li>第三方模块按需导入</li><li>长列表滚动到可视区域动态加载</li><li>图片懒加载</li></ul><p><strong><em>SEO</em> 优化</strong></p><ul><li>预渲染</li><li>服务端渲染 <em>SSR</em></li></ul><p><strong>打包优化</strong></p><ul><li>压缩代码</li><li><em>Tree Shaking/Scope Hoisting</em></li><li>使用 <em>cdn</em> 加载第三方模块</li><li>多线程打包 <em>happypack</em></li><li><em>splitChunks</em> 抽离公共文件</li><li><em>sourceMap</em> 优化</li></ul><p><strong>用户体验</strong></p><ul><li>骨架屏</li><li><em>PWA</em></li></ul><p>还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启 <em>gzip</em> 压缩等。</p></blockquote><h2 id="_31-vue-extend-和-vue-component-的区别是什么" tabindex="-1">31. <strong><em>Vue.extend</em> 和 <em>Vue.component</em> 的区别是什么？</strong> <a class="header-anchor" href="#_31-vue-extend-和-vue-component-的区别是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>Vue.extend</em> 用于创建一个基于 <em>Vue</em> 构造函数的“子类”，其参数应为一个包含组件选项的对象。</p><p><em>Vue.component</em> 用来注册全局组件。</p></blockquote><h2 id="_32-vue-中的-spa-应用如何优化首屏加载速度" tabindex="-1">32. <strong><em>vue</em> 中的 <em>spa</em> 应用如何优化首屏加载速度?</strong> <a class="header-anchor" href="#_32-vue-中的-spa-应用如何优化首屏加载速度" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>优化首屏加载可以从这几个方面开始：</p><ul><li>请求优化：CDN 将第三方的类库放到 CDN 上，能够大幅度减少生产环境中的项目体积，另外 CDN 能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。</li><li>缓存：将长时间不会改变的第三方类库或者静态资源设置为强缓存，将 max-age 设置为一个非常长的时间，再将访问路径加上哈希达到哈希值变了以后保证获取到最新资源，好的缓存策略有助于减轻服务器的压力，并且显著的提升用户的体验</li><li>gzip：开启 gzip 压缩，通常开启 gzip 压缩能够有效的缩小传输资源的大小。</li><li>http2：如果系统首屏同一时间需要加载的静态资源非常多，但是浏览器对同域名的 tcp 连接数量是有限制的(chrome 为 6 个)超过规定数量的 tcp 连接，则必须要等到之前的请求收到响应后才能继续发送，而 http2 则可以在多个 tcp 连接中并发多个请求没有限制，在一些网络较差的环境开启 http2 性能提升尤为明显。</li><li>懒加载：当 url 匹配到相应的路径时，通过 import 动态加载页面组件，这样首屏的代码量会大幅减少，webpack 会把动态加载的页面组件分离成单独的一个 chunk.js 文件</li><li>预渲染：由于浏览器在渲染出页面之前，需要先加载和解析相应的 html、css 和 js 文件，为此会有一段白屏的时间，可以添加loading，或者骨架屏幕尽可能的减少白屏对用户的影响体积优化</li><li>合理使用第三方库：对于一些第三方 ui 框架、类库，尽量使用按需加载，减少打包体积</li><li>使用可视化工具分析打包后的模块体积：webpack-bundle- analyzer 这个插件在每次打包后能够更加直观的分析打包后模块的体积，再对其中比较大的模块进行优化</li><li>提高代码使用率：利用代码分割，将脚本中无需立即调用的代码在代码构建时转变为异步加载的过程</li><li>封装：构建良好的项目架构，按照项目需求就行全局组件，插件，过滤器，指令，utils 等做一 些公共封装，可以有效减少我们的代码量，而且更容易维护资源优化</li><li>图片懒加载：使用图片懒加载可以优化同一时间减少 http 请求开销，避免显示图片导致的画面抖动，提高用户体验</li><li>使用 svg 图标：相对于用一张图片来表示图标，svg 拥有更好的图片质量，体积更小，并且不需要开启额外的 http 请求</li><li>压缩图片：可以使用 image-webpack-loader，在用户肉眼分辨不清的情况下一定程度上压缩图片</li></ul></blockquote><h2 id="_33-移动端如何实现一个比较友好的-header-组件" tabindex="-1">33. <strong>移动端如何实现一个比较友好的 <em>header</em> 组件</strong> <a class="header-anchor" href="#_33-移动端如何实现一个比较友好的-header-组件" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>Header</em> 一般分为左、中、右三个部分，分为三个区域来设计，中间为主标题，每个页面的标题肯定不同，所以可以通过 <em>vue props</em>的方式做成可配置对外进行暴露，左侧大部分页面可能都是回退按钮，但是样式和内容不尽相同，右侧一般都是具有功能性的操作按钮，所以左右两侧可以通过 <em>vue slot</em> 插槽的方式对外暴露以实现多样化，同时也可以提供 <em>default slot</em> 默认插槽来统一页面风格。</p></blockquote><h2 id="_34-既然-vue-通过数据劫持可以精准探测数据变化-为什么还需要虚拟-dom-进行-diff-监测差异" tabindex="-1">34. <strong>既然 <em>Vue</em> 通过数据劫持可以精准探测数据变化，为什么还需要虚拟 <em>DOM</em> 进行 <em>diff</em> 监测差异 ？</strong> <a class="header-anchor" href="#_34-既然-vue-通过数据劫持可以精准探测数据变化-为什么还需要虚拟-dom-进行-diff-监测差异" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>现代前端框架有两种方式侦测变化，一种是 <em>pull</em>，一种是 <em>push</em>。</p><p><em><strong>pull</strong></em></p><p>其代表为 <em>React</em>，我们可以回忆一下 <em>React</em> 是如何侦测到变化的。</p><p>我们通常会用 <em>setState API</em> 显式更新,然后 <em>React</em> 会进行一层层的 <em>Virtual Dom Diff</em> 操作找出差异，然后 <em>Patch</em> 到 <em>DOM</em> 上，<em>React</em> 从一开始就不知道到底是哪发生了变化,只是知道「有变化了」,然后再进行比较暴力的 <em>Diff</em> 操作查找「哪发生变化了」，另外一个代表就是 <em>Angular</em> 的脏检查操作。</p><p><em><strong>push</strong></em></p><p><em>Vue</em> 的响应式系统则是 <em>push</em> 的代表，当 <em>Vue</em> 程序初始化的时候就会对数据 <em>data</em> 进行依赖的收集，一但数据发生变化，响应式系统就会立刻得知，因此 <em>Vue</em> 是一开始就知道是「在哪发生变化了」</p><p>但是这又会产生一个问题，通常绑定一个数据就需要一个 <em>Watcher</em>，一但我们的绑定细粒度过高就会产生大量的 <em>Watcher</em>，这会带来内存以及依赖追踪的开销，而细粒度过低会无法精准侦测变化，因此 <em>Vue</em> 的设计是选择中等细粒度的方案，在组件级别进行 <em>push</em> 侦测的方式，也就是那套响应式系统。</p><p>通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行 <em>Virtual Dom Diff</em> 获取更加具体的差异，而 <em>Virtual Dom Diff</em> 则是 <em>pull</em> 操作，<em>Vue</em> 是 <em>push + pull</em> 结合的方式进行变化侦测的。</p></blockquote><h2 id="_35-vue-为什么没有类似于-react-中-shouldcomponentupdate-的生命周期" tabindex="-1">35. <strong><em>Vue</em> 为什么没有类似于 <em>React</em> 中 <em>shouldComponentUpdate</em> 的生命周期？</strong> <a class="header-anchor" href="#_35-vue-为什么没有类似于-react-中-shouldcomponentupdate-的生命周期" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>根本原因是 <em>Vue</em> 与 <em>React</em> 的变化侦测方式有所不同</p><p><em>React</em> 是 <em>pull</em> 的方式侦测变化，当 <em>React</em> 知道发生变化后，会使用 <em>Virtual Dom Diff</em> 进行差异检测,但是很多组件实际上是肯定不会发生变化的，这个时候需要用 <em>shouldComponentUpdate</em> 进行手动操作来减少 <em>diff</em>，从而提高程序整体的性能。</p><p><em>Vue</em> 是 <em>pull+push</em> 的方式侦测变化的，在一开始就知道那个组件发生了变化，因此在 <em>push</em> 的阶段并不需要手动控制 <em>diff</em>，而组件内部采用的 <em>diff</em> 方式实际上是可以引入类似于 <em>shouldComponentUpdate</em> 相关生命周期的，但是通常合理大小的组件不会有过量的 <em>diff</em>，手动优化的价值有限，因此目前 <em>Vue</em> 并没有考虑引入 <em>shouldComponentUpdate</em> 这种手动优化的生命周期。</p></blockquote><h2 id="_36-vue-中的-key-的作用是什么" tabindex="-1">36. <strong><em>Vue</em> 中的 <em>Key</em> 的作用是什么？</strong> <a class="header-anchor" href="#_36-vue-中的-key-的作用是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><strong><em>key</em> 的作用主要是为了高效的更新虚拟 <em>DOM</em></strong>。另外 <em>vue</em> 中在使用相同标签名元素的过渡切换时，也会使用到 <em>key</em> 属性，其目的也是为了让 <em>vue</em> 可以区分它们，否则 <em>vue</em> 只会替换其内部属性而不会触发过渡效果。</p></blockquote><blockquote><p>解析：</p><p>其实不只是 <em>vue</em>，<em>react</em> 中在执行列表渲染时也会要求给每个组件添加上 <em>key</em> 这个属性。</p><p>要解释 <em>key</em> 的作用，不得不先介绍一下虚拟 <em>DOM</em> 的 <em>Diff</em> 算法了。</p><p>我们知道，<em>vue</em> 和 <em>react</em> 都实现了一套虚拟 <em>DOM</em>，使我们可以不直接操作 <em>DOM</em> 元素，只操作数据便可以重新渲染页面。而隐藏在背后的原理便是其高效的 <em>Diff</em> 算法。</p><p><em>vue</em> 和 <em>react</em> 的虚拟 <em>DOM</em> 的 <em>Diff</em> 算法大致相同，其核心有以下两点：</p><ul><li><p>两个相同的组件产生类似的 <em>DOM</em> 结构，不同的组件产生不同的 <em>DOM</em> 结构。</p></li><li><p>同一层级的一组节点，他们可以通过唯一的 <em>id</em> 进行区分。</p></li></ul><p>基于以上这两点，使得虚拟 <em>DOM</em> 的 <em>Diff</em> 算法的复杂度从 <em>O(n^3)</em> 降到了 <em>O(n)</em>。</p><img src="/vue/diff.png" style="zoom:50%;"><p>当页面的数据发生变化时，<em>Diff</em> 算法只会比较同一层级的节点：</p><ul><li>如果节点类型不同，直接干掉前面的节点，再创建并插入新的节点，不会再比较这个节点以后的子节点了。</li><li>如果节点类型相同，则会重新设置该节点的属性，从而实现节点的更新。</li></ul><p>当某一层有很多相同的节点时，也就是列表节点时，<em>Diff</em> 算法的更新过程默认情况下也是遵循以上原则。</p><p>比如一下这个情况：</p><p><img src="/vue/diff-1.jpg" alt="img"></p><p>我们希望可以在 <em>B</em> 和 <em>C</em> 之间加一个 <em>F</em>，<em>Diff</em> 算法默认执行起来是这样的：</p><p><img src="/vue/diff-2.jpg" alt="img"></p><p>即把 <em>C</em> 更新成 <em>F</em>，<em>D</em> 更新成 <em>C</em>，<em>E</em> 更新成 <em>D</em>，最后再插入 <em>E</em></p><p>是不是很没有效率？</p><p>所以我们需要使用 <em>key</em> 来给每个节点做一个唯一标识，<em>Diff</em> 算法就可以正确的识别此节点，找到正确的位置区插入新的节点。</p><p><img src="/vue/diff-3.jpg" alt="img"></p></blockquote><h2 id="_37-你的接口请求一般放在哪个生命周期中-为什么要这样做" tabindex="-1">37. <strong>你的接口请求一般放在哪个生命周期中？为什么要这样做？</strong> <a class="header-anchor" href="#_37-你的接口请求一般放在哪个生命周期中-为什么要这样做" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>接口请求可以放在钩子函数 <em>created、beforeMount、mounted</em> 中进行调用，因为在这三个钩子函数中，<em>data</em> 已经创建，可以将服务端端返回的数据进行赋值。</p><p>但是推荐在 <em>created</em> 钩子函数中调用异步请求，因为在 <em>created</em> 钩子函数中调用异步请求有以下优点：</p><ul><li>能更快获取到服务端数据，减少页面 <em>loading</em> 时间</li><li><em>SSR</em> 不支持 <em>beforeMount 、mounted</em> 钩子函数，所以放在 <em>created</em> 中有助于代码的一致性</li><li><em>created</em> 是在模板渲染成 <em>html</em> 前调用，即通常初始化某些属性值，然后再渲染成视图。如果在 <em>mounted</em> 钩子函数中请求数据可能导致页面闪屏问题</li></ul></blockquote><h2 id="_38-说一下你对-vue-事件绑定原理的理解" tabindex="-1">38. <strong>说一下你对 <em>vue</em> 事件绑定原理的理解？</strong> <a class="header-anchor" href="#_38-说一下你对-vue-事件绑定原理的理解" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>vue</em> 中的事件绑定是有两种，一种是原生的事件绑定，另一种是组件的事件绑定。</p><p>原生的事件绑定在普通元素上是通过 <em>@click</em> 进行绑定，在组件上是通过 <em>@click.native</em> 进行绑定，组件中的 <em>nativeOn</em> 是等价于 on 的。组件的事件绑定的 @click 是 vue 中自定义的 $on 方法来实现的，必须有 $emit 才可以触发。</p><p><strong>原生事件绑定原理</strong></p><p>在 runtime下的patch.js中createPatchFunction执行了之后再赋值给patch。</p><p>createPatchFunction方法有两个参数，分别是nodeOps存放操作dom节点的方法和modules，modules是有两个数组拼接起来的，modules拼接完的数组中有一个元素就是events，事件添加就发生在这里。</p><p>events元素关联的就是events.js文件，在events中有一个updateDOMListeners方法，在events文件的结尾导出了一个对象，然后对象有一个属性叫做create，这个属性关联的就是updateDOMListeners方法。</p><p>在执行createPatchFunction方法时，就会将这两个参数传入，在createPatchFunction方法中接收了一个参数backend，在该方法中一开始进行backend的解构，就是上面的nodeOps和modules参数，解构完之后进入for循环。</p><p>在createPatchFunction开头定义了一个cbs对象。for循环遍历一个叫hooks的数组。hooks是文件一开头定义的一个数组，其中包括有create，for循环就是在cbs上定义一系列和hooks元素相同的属性，然后键值是一个数组，然后数组内容是modules里面的一些内容。这时就把events文件中导出来的create属性放在了cbs上。</p><p>当我们进入首次渲染的时候，会执行到patch函数里面的createElm方法，这个方法中就会调用invokeCreateHooks函数，用来处理事件系统，这里就是真正准备进行原生事件绑定的入口。invokeCreateHooks方法中，遍历了cbs.create数组里面的内容。然后把cbs.create里面的函数全部都执行一次，在cbs.create其中一个函数就是updateDOMListeners。</p><p>updateDOMListeners就是用来添加事件的方法，在这方法中会根据vnode判断是否有定义一个点击事件。如果没有点击事件就return。有的话就继续执行，给on进行赋值，然后进行一些赋值操作，将vnode.elm赋值给target，elm这个属性就是指向vnode所对应的真实dom节点，这里就是把我们要绑定事件的dom结点进行缓存，接下来执行updateListeners方法。在接下来执行updateListeners方法中调用了一个add的方法，然后在app方法中通过原生addEventListener把事件绑定到dom上。</p><p><strong>组件事件绑定原理</strong></p><p>在组件实例初始化会调用initMixin方法中的Vue.prototype._init，在init函数中，会通过initInternalComponent方法初始化组件信息，将自定义的组件事件放到_parentListeners上，下来就会调用initEvents来初始化组件事件，在initEvents中会实例上添加一个 _event对象，用于保存自定义事件，然后获取到 父组件给 子组件绑定的自定义事件，也就是刚才在初始化组件信息的时候将自定义的组件事件放在了_parentListeners上，这时候vm.$options._parentListeners就是自定义的事件。</p><p>最后进行判断，如果有自定义的组件事件就执行updateComponentListeners方法进行事件绑定，在updateComponentListeners方法中会调用updateListeners方法，并传传一个add方法进行执行，这个add方法里就是$on方法。</p></blockquote><h2 id="_39-说一下-vue-模版编译的原理是什么" tabindex="-1">39. <strong>说一下 <em>vue</em> 模版编译的原理是什么</strong> <a class="header-anchor" href="#_39-说一下-vue-模版编译的原理是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>简单说，<em>Vue</em> 的编译过程就是将 <em>template</em> 转化为 <em>render</em> 函数的过程。会经历以下阶段：</p><ul><li>生成 <em>AST</em> 树</li><li>优化</li><li><em>codegen</em></li></ul><p>首先解析模版，生成 <em>AST</em> 语法树(一种用 <em>JavaScript</em> 对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析，遇到标签、文本的时候都会执行对应的钩子进行相关处理。</p><p><em>Vue</em> 的数据是响应式的，但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化，对应的 <em>DOM</em> 也不会变化。那么优化过程就是深度遍历 <em>AST</em> 树，按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对，对运行时的模板起到很大的优化作用。</p><p>编译的最后一步是将优化后的 <em>AST</em> 树转换为可执行的代码。</p></blockquote><blockquote><p>可以参阅前面第 <em>22</em> 题。</p></blockquote><h2 id="_40-delete-和-vue-delete-删除数组的区别是什么" tabindex="-1">40. <strong><em>delete</em> 和 <em>Vue.delete</em> 删除数组的区别是什么？</strong> <a class="header-anchor" href="#_40-delete-和-vue-delete-删除数组的区别是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>delete</em> 只是被删除的元素变成了 <em>empty/undefined</em> 其他的元素的键值还是不变。 <em>Vue.delete</em> 是直接将元素从数组中完全删除，改变了数组其他元素的键值。</p></blockquote><h2 id="_41-v-on-可以实现监听多个方法么" tabindex="-1">41. <strong><em>v-on</em> 可以实现监听多个方法么？</strong> <a class="header-anchor" href="#_41-v-on-可以实现监听多个方法么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>可以监听多个方法。关于监听多个方法提供了几种不同的写法：</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#A6ACCD;">写法一：</span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">v-on</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">{ 事件类型: 事件处理函数, 事件类型: 事件处理函数 }</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">写法二：</span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@事件类型</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">“事件处理函数”</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@事件类型</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">“事件处理函数”</span><span style="color:#89DDFF;">&gt;&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">写法三：在一个事件里面书写多个事件处理函数</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@事件类型</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">“事件处理函数1，事件处理函数2”</span><span style="color:#89DDFF;">&gt;&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">写法四：在事件处理函数内部调用其他的函数</span></span>
<span class="line"></span></code></pre></div><p>示例代码如下：</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#676E95;font-style:italic;">&lt;!-- v-on在vue2.x中测试,以下两种均可--&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">v-on</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">{ mouseenter: onEnter, mouseleave: onLeave }</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      鼠标进来1</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@mouseenter</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">onEnter</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@mouseleave</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">onLeave</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">鼠标进来2</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#676E95;font-style:italic;">&lt;!-- 一个事件绑定多个函数，按顺序执行，这里分隔函数可以用逗号也可以用分号--&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@click</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">a(), b()</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">点我ab</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@click</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">one()</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">点我onetwothree</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">methods</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">//这里是es6对象里函数写法</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">a</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">a</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">b</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">b</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">one</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">one</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">this.</span><span style="color:#82AAFF;">two</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">this.</span><span style="color:#82AAFF;">three</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">two</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">two</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">three</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">three</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">onEnter</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">mouse enter</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">onLeave</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">mouse leave</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span></code></pre></div></blockquote><h2 id="_42-vue-的数据为什么频繁变化但只会更新一次" tabindex="-1">42. <strong><em>vue</em> 的数据为什么频繁变化但只会更新一次？</strong> <a class="header-anchor" href="#_42-vue-的数据为什么频繁变化但只会更新一次" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>这是因为 <em>vue</em> 的 <em>DOM</em> 更新是一个异步操作，在数据更新后会首先被 <em>set</em> 钩子监听到，但是不会马上执行 <em>DOM</em> 更新，而是在下一轮循环中执行更新。</p><p>具体实现是 <em>vue</em> 中实现了一个 <em>queue</em> 队列用于存放本次事件循环中的所有 <em>watcher</em> 更新，并且同一个 <em>watcher</em> 的更新只会被推入队列一次，并在本轮事件循环的微任务执行结束后执行此更新(<em>UI Render</em> 阶段)，这就是 <em>DOM</em> 只会更新一次的原因。</p><p>这种在缓冲时去除重复数据对于避免不必要的计算和 <em>DOM</em> 操作是非常重要的。然后，在下一个的事件循环“<em>tick</em>”中，<em>vue</em> 刷新队列并执行实际 (已去重的) 工作。<em>vue</em> 在内部对异步队列尝试使用原生的 <em>Promise.then、MutationObserver</em> 和 <em>setImmediate</em>，如果执行环境不支持，则会采用 <em>setTimeout(fn, 0)</em> 代替。</p></blockquote><h2 id="_43-说一下-vue-中-computed-和-methods-的区别是什么" tabindex="-1">43. <strong>说一下 <em>vue</em> 中 <em>computed</em> 和 <em>methods</em> 的区别是什么？</strong> <a class="header-anchor" href="#_43-说一下-vue-中-computed-和-methods-的区别是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>首先从表现形式上面来看， <em>computed</em> 和 <em>methods</em> 的区别大致有下面 <em>4</em> 点：</p><ol><li>在使用时，<em>computed</em> 当做属性使用，而 <em>methods</em> 则当做方法调用</li><li><em>computed</em> 可以具有 <em>getter</em> 和 <em>setter</em>，因此可以赋值，而 <em>methods</em> 不行</li><li><em>computed</em> 无法接收多个参数，而 <em>methods</em> 可以</li><li><em>computed</em> 具有缓存，而 <em>methods</em> 没有</li></ol><p>而如果从底层来看的话， <em>computed</em> 和 <em>methods</em> 在底层实现上面还有很大的区别。</p><p><em>vue</em> 对 <em>methods</em> 的处理比较简单，只需要遍历 <em>methods</em> 配置中的每个属性，将其对应的函数使用 <em>bind</em> 绑定当前组件实例后复制其引用到组件实例中即可</p><p>而 <em>vue</em> 对 <em>computed</em> 的处理会稍微复杂一些。</p><p>具体可以参阅前面第 <em>21</em> 题。</p></blockquote><h2 id="_44-在-vue-中要获取当前时间你会放到-computed-还是-methods-里-抖音直播" tabindex="-1">44. <strong>在 <em>Vue</em> 中要获取当前时间你会放到 <em>computed</em> 还是 <em>methods</em> 里？(抖音直播)</strong> <a class="header-anchor" href="#_44-在-vue-中要获取当前时间你会放到-computed-还是-methods-里-抖音直播" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>放在 <em>computed</em> 里面。因为 <em>computed</em> 只有在它的相关依赖发生改变时才会重新求值。相比而言，方法只要发生重新渲染，<em>methods</em> 调用总会执行所有函数。</p></blockquote><h2 id="_45-在给-vue-中的元素设置-key-值时可以使用-math-的-random-方法么" tabindex="-1">45. <strong>在给 <em>vue</em> 中的元素设置 <em>key</em> 值时可以使用 <em>Math</em> 的 <em>random</em> 方法么？</strong> <a class="header-anchor" href="#_45-在给-vue-中的元素设置-key-值时可以使用-math-的-random-方法么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>random</em> 是生成随机数，有一定概率多个 <em>item</em> 会生成相同的值，不能保证唯一。</p><p>如果是根据数据来生成 <em>item</em>，数据具有 <em>id</em> 属性，那么就可以使用 <em>id</em> 来作为 <em>key</em>。</p><p>如果不是根据数据生成 <em>item</em>，那么最好的方式就是使用时间戳来作为 <em>key</em>。或者使用诸如 <em>uuid</em> 之类的库来生成唯一的 <em>id</em>。</p></blockquote><h2 id="_46-插槽与作用域插槽的区别是什么" tabindex="-1">46. <strong>插槽与作用域插槽的区别是什么？</strong> <a class="header-anchor" href="#_46-插槽与作用域插槽的区别是什么" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>插槽的作用是子组件提供了可替换模板，父组件可以更换模板的内容。</p><p>作用域插槽给了子组件将数据返给父组件的能力，子组件一样可以复用，同时父组件也可以重新组织内容和样式。</p></blockquote><h2 id="_47-vue-中相同逻辑如何进行抽离" tabindex="-1">47. <strong><em>vue</em> 中相同逻辑如何进行抽离？</strong> <a class="header-anchor" href="#_47-vue-中相同逻辑如何进行抽离" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>可以使用 <em>vue</em> 里面的混入（<em>mixin</em>）技术。混入（<em>mixin</em>）提供了一种非常灵活的方式，来将 <em>vue</em> 中相同的业务逻辑进行抽离。</p><p>例如：</p><ul><li>在 <em>data</em> 中有很多是公用数据</li><li>引用封装好的组件也都是一样的</li><li><em>methods、watch、computed</em> 中也都有大量的重复代码</li></ul><p>当然这个时候可以将所有的代码重复去写来实现功能，但是我们并不不推荐使用这种方式，无论是工作量、工作效率和后期维护来说都是不建议的，这个时候 <em>mixin</em> 就可以大展身手了。</p><p>一个混入对象可以包含任意组件选项。当组件使用混入对象时，所有混入对象的选项将被“混合”进入该组件本身的选项。说白了就是给每个生命周期，函数等等中间加入一些公共逻辑。</p><p><strong>混入技术特点</strong></p><ul><li>当组件和混入对象含有同名选项时，这些选项将以恰当的方式进行“合并”。比如，数据对象在内部会进行递归合并，并在发生冲突时以组件数据优先。</li><li>同名钩子函数将合并为一个数组，因此都将被调用。另外，混入对象的钩子将在组件自身钩子之前调用。</li><li>值为对象的选项，例如 <em>methods、components</em> 和 <em>directives</em>，将被合并为同一个对象。两个对象键名冲突时，取组件对象的键值对。</li></ul></blockquote><h2 id="_48-如何监听-pushstate-和-replacestate-的变化呢" tabindex="-1">48. <strong>如何监听 <em>pushstate</em> 和 <em>replacestate</em> 的变化呢？</strong> <a class="header-anchor" href="#_48-如何监听-pushstate-和-replacestate-的变化呢" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><em>History.replaceState</em> 和 <em>pushState</em> 不会触发 <em>popstate</em> 事件，所以我们可以通过在方法中创建一个新的全局事件来实现 <em>pushstate</em> 和 <em>replacestate</em> 变化的监听。</p><p>具体做法为：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#C792EA;">var</span><span style="color:#A6ACCD;"> _wr </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">type</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">orig</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">history</span><span style="color:#F07178;">[</span><span style="color:#A6ACCD;">type</span><span style="color:#F07178;">]</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">function</span><span style="color:#89DDFF;">()</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">rv</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">orig</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">apply</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">this,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">arguments</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">     </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">e</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">new</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">Event</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">type</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">e</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">arguments</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">arguments</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">window</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">dispatchEvent</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">e</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">rv</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">};</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span>
<span class="line"><span style="color:#A6ACCD;">history</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">pushState </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">_wr</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">pushState</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">history</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">replaceState </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">_wr</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">replaceState</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span></code></pre></div><p>这样就创建了 <em>2</em> 个全新的事件，事件名为 <em>pushState</em> 和 <em>replaceState</em>，我们就可以在全局监听：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#A6ACCD;">window</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">addEventListener</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">replaceState</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">THEY DID IT AGAIN! replaceState 111111</span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">window</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">addEventListener</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">pushState</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">THEY DID IT AGAIN! pushState 2222222</span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span></code></pre></div><p>这样就可以监听到 <em>pushState</em> 和 <em>replaceState</em> 行为。</p></blockquote><h2 id="_49-说一下-vue3-0-是如何变得更快的" tabindex="-1">49. <strong>说一下 <em>vue3.0</em> 是如何变得更快的？</strong> <a class="header-anchor" href="#_49-说一下-vue3-0-是如何变得更快的" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p><strong>优化 <em>Diff</em> 算法</strong></p><p>相比 <em>Vue 2</em>，<em>Vue 3</em> 采用了更加优化的渲染策略。去掉不必要的虚拟 <em>DOM</em> 树遍历和属性比较，因为这在更新期间往往会产生最大的性能开销。</p><p>这里有三个主要的优化：</p><ul><li>首先，在 <em>DOM</em> 树级别。</li></ul><p>在没有动态改变节点结构的模板指令（例如 <em>v-if</em> 和 <em>v-for</em>）的情况下，节点结构保持完全静态。</p><p>当更新节点时，不再需要递归遍历 <em>DOM</em> 树。所有的动态绑定部分将在一个平面数组中跟踪。这种优化通过将需要执行的树遍历量减少一个数量级来规避虚拟 <em>DOM</em> 的大部分开销。</p><ul><li><p>其次，编译器积极地检测模板中的静态节点、子树甚至数据对象，并在生成的代码中将它们提升到渲染函数之外。这样可以避免在每次渲染时重新创建这些对象，从而大大提高内存使用率并减少垃圾回收的频率。</p></li><li><p>第三，在元素级别。</p></li></ul><p>编译器还根据需要执行的更新类型，为每个具有动态绑定的元素生成一个优化标志。</p><p>例如，具有动态类绑定和许多静态属性的元素将收到一个标志，提示只需要进行类检查。运行时将获取这些提示并采用专用的快速路径。</p><p>综合起来，这些技术大大改进了渲染更新基准，<em>Vue 3.0</em> 有时占用的 <em>CPU</em> 时间不到 <em>Vue 2</em> 的十分之一。</p><p><strong>体积变小</strong></p><p>重写后的 <em>Vue</em> 支持了 <em>tree-shaking</em>，像修剪树叶一样把不需要的东西给修剪掉，使 <em>Vue 3.0</em> 的体积更小。</p><p>需要的模块才会打入到包里，优化后的 <em>Vue 3.0</em> 的打包体积只有原来的一半（<em>13kb</em>）。哪怕把所有的功能都引入进来也只有 <em>23kb</em>，依然比 <em>Vue 2.x</em> 更小。像 <em>keep-alive、transition</em> 甚至 <em>v-for</em> 等功能都可以按需引入。</p><p>并且 <em>Vue 3.0</em> 优化了打包方法，使得打包后的 <em>bundle</em> 的体积也更小。</p><p>官方所给出的一份惊艳的数据：打包大小减少 <em>41%</em>，初次渲染快 <em>55%</em>，更新快 <em>133%</em>，内存使用减少 <em>54%</em>。</p></blockquote><h2 id="_50-说一说自定义指令有哪些生命周期" tabindex="-1">50. 说一说自定义指令有哪些生命周期？ <a class="header-anchor" href="#_50-说一说自定义指令有哪些生命周期" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>自定义指令的生命周期，有 5 个事件钩子，可以设置指令在某一个事件发生时的具体行为：</p><ul><li>bind: 只调用一次，指令第一次绑定到元素时调用，用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。</li><li>inserted: 被绑定元素插入父节点时调用（父节点存在即可调用，不必存在于 document 中）。</li><li>update: 被绑定元素所在的模板更新时调用，而不论绑定值是否变化。通过比较更新前后的绑定值，可以忽略不必要的模板更新（详细的钩子函数参数见下）。</li><li>componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。</li><li>unbind: 只调用一次， 指令与元素解绑时调用。</li></ul><p>钩子函数的参数 (包括 el，binding，vnode，oldVnode)</p><ul><li>el: 指令所绑定的元素，可以用来直接操作 DOM 。</li><li>binding: 一个对象，包含以下属性：name: 指令名、value: 指令的绑定值、oldValue: 指令绑定的前一个值、expression: 绑定值的字符串形式、arg: 传给指令的参数、modifiers: 一个包含修饰符的对象。</li><li>vnode: Vue 编译生成的虚拟节点。</li><li>oldVnode: 上一个虚拟节点，仅在 update 和 componentUpdated 钩子中可用。</li></ul></blockquote><h2 id="_51-说一说相比-vue3-x-对比-vue2-x-变化" tabindex="-1">51. 说一说相比 <em>vue3.x</em> 对比 <em>vue2.x</em> 变化 <a class="header-anchor" href="#_51-说一说相比-vue3-x-对比-vue2-x-变化" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><ol><li>源码组织方式变化：使用 TS 重写</li><li>支持 Composition API：基于函数的API，更加灵活组织组件逻辑（vue2用的是options api）</li><li>响应式系统提升：Vue3中响应式数据原理改成proxy，可监听动态新增删除属性，以及数组变化</li><li>编译优化：vue2通过标记静态根节点优化diff，Vue3 标记和提升所有静态根节点，diff的时候只需要对比动态节点内容</li><li>打包体积优化：移除了一些不常用的api（inline-template、filter）</li><li>生命周期的变化：使用setup代替了之前的beforeCreate和created</li><li>Vue3 的 template 模板支持多个根标签</li><li>Vuex状态管理：创建实例的方式改变,Vue2为new Store , Vue3为createStore</li><li>Route 获取页面实例与路由信息：vue2通过this获取router实例，vue3通过使用 getCurrentInstance/ userRoute和userRouter方法获取当前组件实例</li><li>Props 的使用变化：vue2 通过 this 获取 props 里面的内容，vue3 直接通过 props</li><li>父子组件传值：vue3 在向父组件传回数据时，如使用的自定义名称，如 backData，则需要在 emits 中定义一下</li></ol></blockquote><h2 id="_52-vue-为什么采用异步渲染" tabindex="-1">52. <em>vue</em> 为什么采用异步渲染 <a class="header-anchor" href="#_52-vue-为什么采用异步渲染" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><p>因为如果不采用异步更新，那么每次更新数据都会对当前组件进行重新渲染；所以为了性能考虑，<em>Vue</em> 会在本轮数据更新后，再去异步更新视图。</p><p>异步渲染的原理：</p><ol><li>调用 <em>notify( )</em> 方法，通知 <em>watcher</em> 进行更新操作</li><li>依次调用 watcher 的 update 方法</li><li>对 watcher 进行去重操作（通过id）放到队列里</li><li>执行完后异步清空这个队列，nextTick（flushSchedulerQueue）进行批量更新操作</li></ol></blockquote><h2 id="_53-组件中写-name-选项有哪些好处" tabindex="-1">53. 组件中写 <em>name</em> 选项有哪些好处 <a class="header-anchor" href="#_53-组件中写-name-选项有哪些好处" aria-hidden="true">#</a></h2><blockquote><p>参考答案：</p><ol><li>可以通过名字找到对应的组件（ 递归组件：组件自身调用自身 ）</li><li>可以通过 <em>name</em> 属性实现缓存功能（<em>keep-alive</em>）</li><li>可以通过 <em>name</em> 来识别组件（跨级组件通信时非常重要）</li><li>使用 <em>vue-devtools</em> 调试工具里显示的组见名称是由 <em>vue</em> 中组件 <em>name</em> 决定的</li></ol></blockquote></div></div></main><!--[--><!--]--><footer class="VPDocFooter" data-v-ae7d01fc data-v-2813752b><!----><div class="prev-next" data-v-2813752b><div class="pager" data-v-2813752b><a class="pager-link prev" href="/interview/promise.html" data-v-2813752b><span class="desc" data-v-2813752b>Previous page</span><span class="title" data-v-2813752b>promise</span></a></div><div class="has-prev pager" data-v-2813752b><a class="pager-link next" href="/interview/%E5%B7%A5%E7%A8%8B%E5%8C%96.html" data-v-2813752b><span class="desc" data-v-2813752b>Next page</span><span class="title" data-v-2813752b>工程化</span></a></div></div></footer><!--[--><!--]--></div></div></div></div></div><!----><!--[--><!--]--></div></div>
    <script>__VP_HASH_MAP__ = JSON.parse("{\"article_index.md\":\"857a285c\",\"article_js 修改 css 变量.md\":\"f79a90a6\",\"alg_index.md\":\"e3a92443\",\"article_uniapp 原生开发细节.md\":\"1f0effae\",\"article_uniapp真机调试列表不显示手机.md\":\"8e696be7\",\"article_qiankun 微应用实践（续-1）.md\":\"2ce41ff7\",\"article_uniapp 混合开发注意事项.md\":\"3a3ed44f\",\"article_uniapp使用安卓模拟器真机调试.md\":\"b16535a3\",\"engineering_index.md\":\"94e96b75\",\"javascript_index.md\":\"aa65c718\",\"index.md\":\"3445c9d9\",\"vue_reactive原理.md\":\"d958340a\",\"article_vue组件开发规范.md\":\"eca49209\",\"net_index.md\":\"ba8c0037\",\"node_复制操作.md\":\"9eddc4d4\",\"article_不同版本 axios 的 content-type.md\":\"f29fdcab\",\"node_index.md\":\"91a4e7d9\",\"vue_index.md\":\"259b0a38\",\"react_index.md\":\"3261342c\",\"interview_index.md\":\"b87fd9dd\",\"vue_封装一个loading控制器.md\":\"19b31069\",\"node_执行 cmd.md\":\"fc1cb5f7\",\"article_前端读写文件.md\":\"0187a051\",\"vue_computed 的 set 用法.md\":\"f66e4f74\",\"article_qiankun 微应用实践（上）.md\":\"5a4ba0c0\",\"vue_ref原理.md\":\"006339e2\",\"article_qiankun 微应用实践（下）.md\":\"0a30ca2d\",\"javascript_柯里化.md\":\"625112bc\",\"article_vitepress常用语法.md\":\"67339de5\",\"engineering_webpack的cachegroups.md\":\"1d6a7f5b\",\"vue_父子组件生命周期顺序.md\":\"ea9b1339\",\"interview_浏览器.md\":\"e20c4f30\",\"javascript_promise.md\":\"e0424afd\",\"engineering_webpack3升级4.md\":\"19d0ea18\",\"article_问题排查之路.md\":\"d663183f\",\"node_删除操作.md\":\"ddef11ba\",\"engineering_webpack 动态引入的坑.md\":\"9d587510\",\"article_封装一个进度管理组件.md\":\"870757fa\",\"engineering_rollup开发esmodule.md\":\"65034df8\",\"article_单词按a-z排序.md\":\"40e5a040\",\"interview_promise.md\":\"613f982f\",\"node_压缩和解压.md\":\"daac03e4\",\"interview_vue.md\":\"76dce706\",\"interview_工程化.md\":\"deda9421\",\"interview_网络.md\":\"30fcaab5\",\"article_搬砖注意事项.md\":\"a594fa21\",\"interview_css-note.md\":\"d48d4f85\",\"article_vitepress emoji.md\":\"1d769177\",\"interview_javascript.md\":\"47fbffd4\"}")</script>
    <script type="module" async src="/assets/app.db63b7d7.js"></script>
    
  </body>
</html>